aboutsummaryrefslogtreecommitdiff
path: root/src/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/util')
-rw-r--r--src/util/Makefile.am440
-rw-r--r--src/util/Makefile.in1945
-rw-r--r--src/util/bandwidth.c328
-rw-r--r--src/util/bio.c525
-rw-r--r--src/util/client.c1216
-rw-r--r--src/util/common_allocation.c359
-rw-r--r--src/util/common_endian.c94
-rw-r--r--src/util/common_logging.c1056
-rw-r--r--src/util/configuration.c1287
-rw-r--r--src/util/connection.c1626
-rw-r--r--src/util/container_bloomfilter.c858
-rw-r--r--src/util/container_heap.c550
-rw-r--r--src/util/container_meta_data.c1180
-rw-r--r--src/util/container_multihashmap.c493
-rw-r--r--src/util/container_slist.c387
-rw-r--r--src/util/crypto_aes.c185
-rw-r--r--src/util/crypto_crc.c171
-rw-r--r--src/util/crypto_hash.c647
-rw-r--r--src/util/crypto_hkdf.c299
-rw-r--r--src/util/crypto_kdf.c89
-rw-r--r--src/util/crypto_ksk.c769
-rw-r--r--src/util/crypto_random.c334
-rw-r--r--src/util/crypto_rsa.c967
-rw-r--r--src/util/disk.c2505
-rw-r--r--src/util/disk.h44
-rw-r--r--src/util/getopt.c1050
-rw-r--r--src/util/getopt_helpers.c290
-rw-r--r--src/util/gnunet-config-diff.c23
-rw-r--r--src/util/gnunet-resolver.c158
-rw-r--r--src/util/gnunet-service-resolver.c584
-rw-r--r--src/util/helper.c506
-rw-r--r--src/util/load.c260
-rw-r--r--src/util/network.c1662
-rw-r--r--src/util/os_installation.c538
-rw-r--r--src/util/os_network.c270
-rw-r--r--src/util/os_priority.c1826
-rw-r--r--src/util/peer.c241
-rw-r--r--src/util/perf_crypto_hash.c70
-rw-r--r--src/util/plugin.c361
-rw-r--r--src/util/program.c261
-rw-r--r--src/util/pseudonym.c602
-rw-r--r--src/util/resolver.conf.in22
-rw-r--r--src/util/resolver.h69
-rw-r--r--src/util/resolver_api.c972
-rw-r--r--src/util/scheduler.c1696
-rw-r--r--src/util/server.c1415
-rw-r--r--src/util/server_mst.c321
-rw-r--r--src/util/server_nc.c450
-rw-r--r--src/util/server_tc.c247
-rw-r--r--src/util/service.c1840
-rw-r--r--src/util/signal.c98
-rw-r--r--src/util/strings.c633
-rw-r--r--src/util/test_bio.c417
-rw-r--r--src/util/test_client.c210
-rw-r--r--src/util/test_common_allocation.c112
-rw-r--r--src/util/test_common_endian.c42
-rw-r--r--src/util/test_common_logging.c99
-rw-r--r--src/util/test_common_logging_dummy.c98
-rw-r--r--src/util/test_common_logging_runtime_loglevels.c384
-rw-r--r--src/util/test_configuration.c553
-rw-r--r--src/util/test_configuration_data.conf30
-rw-r--r--src/util/test_connection.c208
-rw-r--r--src/util/test_connection_addressing.c208
-rw-r--r--src/util/test_connection_receive_cancel.c158
-rw-r--r--src/util/test_connection_timeout.c154
-rw-r--r--src/util/test_connection_timeout_no_connect.c101
-rw-r--r--src/util/test_connection_transmit_cancel.c101
-rw-r--r--src/util/test_container_bloomfilter.c244
-rw-r--r--src/util/test_container_heap.c290
-rw-r--r--src/util/test_container_meta_data.c347
-rw-r--r--src/util/test_container_multihashmap.c105
-rw-r--r--src/util/test_container_slist.c160
-rw-r--r--src/util/test_crypto_aes.c175
-rw-r--r--src/util/test_crypto_aes_weak.c202
-rw-r--r--src/util/test_crypto_crc.c221
-rw-r--r--src/util/test_crypto_hash.c166
-rw-r--r--src/util/test_crypto_hkdf.c351
-rw-r--r--src/util/test_crypto_ksk.c260
-rw-r--r--src/util/test_crypto_random.c71
-rw-r--r--src/util/test_crypto_rsa.c334
-rw-r--r--src/util/test_disk.c283
-rw-r--r--src/util/test_getopt.c216
-rw-r--r--src/util/test_os_network.c86
-rw-r--r--src/util/test_os_priority.c69
-rw-r--r--src/util/test_os_start_process.c194
-rw-r--r--src/util/test_peer.c139
-rw-r--r--src/util/test_plugin.c79
-rw-r--r--src/util/test_plugin_plug.c42
-rw-r--r--src/util/test_program.c121
-rw-r--r--src/util/test_program_data.conf0
-rw-r--r--src/util/test_pseudonym.c191
-rw-r--r--src/util/test_pseudonym_data.conf7
-rw-r--r--src/util/test_resolver_api.c437
-rw-r--r--src/util/test_resolver_api_data.conf8
-rw-r--r--src/util/test_scheduler.c266
-rw-r--r--src/util/test_scheduler_delay.c103
-rw-r--r--src/util/test_server.c222
-rw-r--r--src/util/test_server_disconnect.c180
-rw-r--r--src/util/test_server_with_client.c224
-rw-r--r--src/util/test_server_with_client_unix.c212
-rw-r--r--src/util/test_service.c287
-rw-r--r--src/util/test_service_data.conf29
-rw-r--r--src/util/test_strings.c117
-rw-r--r--src/util/test_time.c244
-rw-r--r--src/util/time.c521
-rw-r--r--src/util/util.conf16
-rw-r--r--src/util/win.cc1266
-rw-r--r--src/util/winproc.c338
108 files changed, 46517 insertions, 0 deletions
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
new file mode 100644
index 0000000..cded34d
--- /dev/null
+++ b/src/util/Makefile.am
@@ -0,0 +1,440 @@
+INCLUDES = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include
+
+plugindir = $(libdir)/gnunet
+
+pkgcfgdir= $(pkgdatadir)/config.d/
+
+dist_pkgcfg_DATA = \
+ util.conf
+
+pkgcfg_DATA = \
+ resolver.conf
+
+if MINGW
+noinst_LTLIBRARIES = \
+ libgnunetutilwin.la
+libgnunetutilwin_la_SOURCES = \
+ win.cc \
+ winproc.c
+libgnunetutilwin_la_LDFLAGS = \
+ -no-undefined -Wl,--export-all-symbols
+libgnunetutilwin_la_LIBADD = \
+ -lshell32 -liconv -lstdc++ \
+ -lcomdlg32 -lgdi32 -liphlpapi
+WINLIB = libgnunetutilwin.la
+endif
+
+if !MINGW
+ SERVER_CLIENT_UNIX = test_server_with_client_unix
+endif
+
+if USE_COVERAGE
+ AM_CFLAGS = --coverage -O0
+ XLIB = -lgcov
+endif
+
+noinst_PROGRAMS = \
+ gnunet-config-diff \
+ test_common_logging_dummy
+
+gnunet_config_diff_SOURCES = \
+ gnunet-config-diff.c
+gnunet_config_diff_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+gnunet_config_diff_DEPENDENCIES = \
+ libgnunetutil.la
+
+test_common_logging_dummy_SOURCES = \
+ test_common_logging_dummy.c
+test_common_logging_dummy_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+test_common_logging_dummy_DEPENDENCIES = \
+ libgnunetutil.la
+
+lib_LTLIBRARIES = libgnunetutil.la
+
+libgnunetutil_la_SOURCES = \
+ bandwidth.c \
+ bio.c \
+ client.c \
+ common_allocation.c \
+ common_endian.c \
+ common_logging.c \
+ configuration.c \
+ connection.c \
+ container_bloomfilter.c \
+ container_heap.c \
+ container_meta_data.c \
+ container_multihashmap.c \
+ container_slist.c \
+ crypto_aes.c \
+ crypto_crc.c \
+ crypto_hash.c \
+ crypto_hkdf.c \
+ crypto_kdf.c \
+ crypto_ksk.c \
+ crypto_random.c \
+ crypto_rsa.c \
+ disk.c \
+ disk.h \
+ getopt.c \
+ getopt_helpers.c \
+ helper.c \
+ load.c \
+ network.c \
+ os_installation.c \
+ os_network.c \
+ os_priority.c \
+ peer.c \
+ plugin.c \
+ program.c \
+ pseudonym.c \
+ resolver_api.c resolver.h \
+ scheduler.c \
+ server.c \
+ server_mst.c \
+ server_nc.c \
+ server_tc.c \
+ service.c \
+ signal.c \
+ strings.c \
+ time.c
+
+
+libgnunetutil_la_LIBADD = \
+ $(GCLIBADD) $(WINLIB) \
+ $(LIBGCRYPT_LIBS) \
+ $(LTLIBICONV) \
+ -lltdl -lz $(XLIB)
+
+libgnunetutil_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) \
+ -version-info 7:0:0
+
+
+bin_PROGRAMS = \
+ gnunet-service-resolver \
+ gnunet-resolver
+
+gnunet_service_resolver_SOURCES = \
+ gnunet-service-resolver.c
+gnunet_service_resolver_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+gnunet_service_resolver_DEPENDENCIES = \
+ libgnunetutil.la
+
+
+gnunet_resolver_SOURCES = \
+ gnunet-resolver.c
+gnunet_resolver_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+gnunet_resolver_DEPENDENCIES = \
+ libgnunetutil.la
+
+plugin_LTLIBRARIES = \
+ libgnunet_plugin_test.la
+
+libgnunet_plugin_test_la_SOURCES = \
+ test_plugin_plug.c
+libgnunet_plugin_test_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+if HAVE_BENCHMARKS
+ BENCHMARKS = \
+ perf_crypto_hash
+endif
+
+check_PROGRAMS = \
+ test_bio \
+ test_client \
+ test_common_allocation \
+ test_common_endian \
+ test_common_logging \
+ test_configuration \
+ test_container_bloomfilter \
+ test_container_meta_data \
+ test_container_multihashmap \
+ test_container_heap \
+ test_container_slist \
+ test_crypto_aes \
+ test_crypto_aes_weak \
+ test_crypto_crc \
+ test_crypto_hash \
+ test_crypto_hkdf \
+ test_crypto_ksk \
+ test_crypto_random \
+ test_crypto_rsa \
+ test_disk \
+ test_getopt \
+ test_connection \
+ test_connection_addressing \
+ test_connection_receive_cancel \
+ test_connection_timeout \
+ test_connection_timeout_no_connect \
+ test_connection_transmit_cancel \
+ test_os_network \
+ test_os_priority \
+ test_peer \
+ test_plugin \
+ test_program \
+ test_pseudonym \
+ test_resolver_api \
+ test_scheduler \
+ test_scheduler_delay \
+ test_server \
+ test_server_disconnect \
+ test_server_with_client \
+ $(SERVER_CLIENT_UNIX) \
+ test_service \
+ test_strings \
+ test_time \
+ $(BENCHMARKS) \
+ test_os_start_process \
+ test_common_logging_runtime_loglevels
+
+if ENABLE_TEST_RUN
+TESTS = $(check_PROGRAMS)
+endif
+
+test_bio_SOURCES = \
+ test_bio.c
+test_bio_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+
+test_os_start_process_SOURCES = \
+ test_os_start_process.c
+test_os_start_process_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+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_common_logging_runtime_loglevels_SOURCES = \
+ test_common_logging_runtime_loglevels.c
+test_common_logging_runtime_loglevels_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 -lextractor
+
+test_container_multihashmap_SOURCES = \
+ test_container_multihashmap.c
+test_container_multihashmap_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_container_heap_SOURCES = \
+ test_container_heap.c
+test_container_heap_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_container_slist_SOURCES = \
+ test_container_slist.c
+test_container_slist_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 \
+ $(LIBGCRYPT_LIBS)
+
+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_hkdf_SOURCES = \
+ test_crypto_hkdf.c
+test_crypto_hkdf_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_connection_SOURCES = \
+ test_connection.c
+test_connection_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_addressing_SOURCES = \
+ test_connection_addressing.c
+test_connection_addressing_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_receive_cancel_SOURCES = \
+ test_connection_receive_cancel.c
+test_connection_receive_cancel_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_timeout_SOURCES = \
+ test_connection_timeout.c
+test_connection_timeout_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_timeout_no_connect_SOURCES = \
+ test_connection_timeout_no_connect.c
+test_connection_timeout_no_connect_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_transmit_cancel_SOURCES = \
+ test_connection_transmit_cancel.c
+test_connection_transmit_cancel_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_peer_SOURCES = \
+ test_peer.c
+test_peer_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_resolver_api_SOURCES = \
+ test_resolver_api.c
+test_resolver_api_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_server_with_client_unix_SOURCES = \
+ test_server_with_client_unix.c
+test_server_with_client_unix_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_program_data.conf \
+ test_pseudonym_data.conf \
+ test_resolver_api_data.conf \
+ test_service_data.conf
diff --git a/src/util/Makefile.in b/src/util/Makefile.in
new file mode 100644
index 0000000..3c82fbf
--- /dev/null
+++ b/src/util/Makefile.in
@@ -0,0 +1,1945 @@
+# Makefile.in generated by automake 1.11.1 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation,
+# Inc.
+# This Makefile.in 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.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+
+
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+target_triplet = @target@
+noinst_PROGRAMS = gnunet-config-diff$(EXEEXT) \
+ test_common_logging_dummy$(EXEEXT)
+bin_PROGRAMS = gnunet-service-resolver$(EXEEXT) \
+ gnunet-resolver$(EXEEXT)
+check_PROGRAMS = test_bio$(EXEEXT) test_client$(EXEEXT) \
+ test_common_allocation$(EXEEXT) test_common_endian$(EXEEXT) \
+ test_common_logging$(EXEEXT) test_configuration$(EXEEXT) \
+ test_container_bloomfilter$(EXEEXT) \
+ test_container_meta_data$(EXEEXT) \
+ test_container_multihashmap$(EXEEXT) \
+ test_container_heap$(EXEEXT) test_container_slist$(EXEEXT) \
+ test_crypto_aes$(EXEEXT) test_crypto_aes_weak$(EXEEXT) \
+ test_crypto_crc$(EXEEXT) test_crypto_hash$(EXEEXT) \
+ test_crypto_hkdf$(EXEEXT) test_crypto_ksk$(EXEEXT) \
+ test_crypto_random$(EXEEXT) test_crypto_rsa$(EXEEXT) \
+ test_disk$(EXEEXT) test_getopt$(EXEEXT) \
+ test_connection$(EXEEXT) test_connection_addressing$(EXEEXT) \
+ test_connection_receive_cancel$(EXEEXT) \
+ test_connection_timeout$(EXEEXT) \
+ test_connection_timeout_no_connect$(EXEEXT) \
+ test_connection_transmit_cancel$(EXEEXT) \
+ test_os_network$(EXEEXT) test_os_priority$(EXEEXT) \
+ test_peer$(EXEEXT) test_plugin$(EXEEXT) test_program$(EXEEXT) \
+ test_pseudonym$(EXEEXT) test_resolver_api$(EXEEXT) \
+ test_scheduler$(EXEEXT) test_scheduler_delay$(EXEEXT) \
+ test_server$(EXEEXT) test_server_disconnect$(EXEEXT) \
+ test_server_with_client$(EXEEXT) $(am__EXEEXT_1) \
+ test_service$(EXEEXT) test_strings$(EXEEXT) test_time$(EXEEXT) \
+ $(am__EXEEXT_2) test_os_start_process$(EXEEXT) \
+ test_common_logging_runtime_loglevels$(EXEEXT)
+subdir = src/util
+DIST_COMMON = $(dist_pkgcfg_DATA) $(srcdir)/Makefile.am \
+ $(srcdir)/Makefile.in $(srcdir)/resolver.conf.in
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/absolute-header.m4 \
+ $(top_srcdir)/m4/align.m4 $(top_srcdir)/m4/argz.m4 \
+ $(top_srcdir)/m4/gettext.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \
+ $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/libtool.m4 \
+ $(top_srcdir)/m4/libunistring.m4 $(top_srcdir)/m4/ltdl.m4 \
+ $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
+ $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
+ $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/acinclude.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/gnunet_config.h
+CONFIG_CLEAN_FILES = resolver.conf
+CONFIG_CLEAN_VPATH_FILES =
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__installdirs = "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" \
+ "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)" \
+ "$(DESTDIR)$(pkgcfgdir)"
+LTLIBRARIES = $(lib_LTLIBRARIES) $(noinst_LTLIBRARIES) \
+ $(plugin_LTLIBRARIES)
+libgnunet_plugin_test_la_LIBADD =
+am_libgnunet_plugin_test_la_OBJECTS = test_plugin_plug.lo
+libgnunet_plugin_test_la_OBJECTS = \
+ $(am_libgnunet_plugin_test_la_OBJECTS)
+AM_V_lt = $(am__v_lt_$(V))
+am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
+am__v_lt_0 = --silent
+libgnunet_plugin_test_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libgnunet_plugin_test_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+am__DEPENDENCIES_1 =
+libgnunetutil_la_DEPENDENCIES = $(WINLIB) $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
+am_libgnunetutil_la_OBJECTS = bandwidth.lo bio.lo client.lo \
+ common_allocation.lo common_endian.lo common_logging.lo \
+ configuration.lo connection.lo container_bloomfilter.lo \
+ container_heap.lo container_meta_data.lo \
+ container_multihashmap.lo container_slist.lo crypto_aes.lo \
+ crypto_crc.lo crypto_hash.lo crypto_hkdf.lo crypto_kdf.lo \
+ crypto_ksk.lo crypto_random.lo crypto_rsa.lo disk.lo getopt.lo \
+ getopt_helpers.lo helper.lo load.lo network.lo \
+ os_installation.lo os_network.lo os_priority.lo peer.lo \
+ plugin.lo program.lo pseudonym.lo resolver_api.lo scheduler.lo \
+ server.lo server_mst.lo server_nc.lo server_tc.lo service.lo \
+ signal.lo strings.lo time.lo
+libgnunetutil_la_OBJECTS = $(am_libgnunetutil_la_OBJECTS)
+libgnunetutil_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CCLD) \
+ $(AM_CFLAGS) $(CFLAGS) $(libgnunetutil_la_LDFLAGS) $(LDFLAGS) \
+ -o $@
+libgnunetutilwin_la_DEPENDENCIES =
+am__libgnunetutilwin_la_SOURCES_DIST = win.cc winproc.c
+@MINGW_TRUE@am_libgnunetutilwin_la_OBJECTS = win.lo winproc.lo
+libgnunetutilwin_la_OBJECTS = $(am_libgnunetutilwin_la_OBJECTS)
+libgnunetutilwin_la_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX \
+ $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=link $(CXXLD) \
+ $(AM_CXXFLAGS) $(CXXFLAGS) $(libgnunetutilwin_la_LDFLAGS) \
+ $(LDFLAGS) -o $@
+@MINGW_TRUE@am_libgnunetutilwin_la_rpath =
+@MINGW_FALSE@am__EXEEXT_1 = test_server_with_client_unix$(EXEEXT)
+@HAVE_BENCHMARKS_TRUE@am__EXEEXT_2 = perf_crypto_hash$(EXEEXT)
+PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS)
+am_gnunet_config_diff_OBJECTS = gnunet-config-diff.$(OBJEXT)
+gnunet_config_diff_OBJECTS = $(am_gnunet_config_diff_OBJECTS)
+am_gnunet_resolver_OBJECTS = gnunet-resolver.$(OBJEXT)
+gnunet_resolver_OBJECTS = $(am_gnunet_resolver_OBJECTS)
+am_gnunet_service_resolver_OBJECTS = \
+ gnunet-service-resolver.$(OBJEXT)
+gnunet_service_resolver_OBJECTS = \
+ $(am_gnunet_service_resolver_OBJECTS)
+am_perf_crypto_hash_OBJECTS = perf_crypto_hash.$(OBJEXT)
+perf_crypto_hash_OBJECTS = $(am_perf_crypto_hash_OBJECTS)
+perf_crypto_hash_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_bio_OBJECTS = test_bio.$(OBJEXT)
+test_bio_OBJECTS = $(am_test_bio_OBJECTS)
+test_bio_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_client_OBJECTS = test_client.$(OBJEXT)
+test_client_OBJECTS = $(am_test_client_OBJECTS)
+test_client_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_common_allocation_OBJECTS = test_common_allocation.$(OBJEXT)
+test_common_allocation_OBJECTS = $(am_test_common_allocation_OBJECTS)
+test_common_allocation_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_common_endian_OBJECTS = test_common_endian.$(OBJEXT)
+test_common_endian_OBJECTS = $(am_test_common_endian_OBJECTS)
+test_common_endian_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_common_logging_OBJECTS = test_common_logging.$(OBJEXT)
+test_common_logging_OBJECTS = $(am_test_common_logging_OBJECTS)
+test_common_logging_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_common_logging_dummy_OBJECTS = \
+ test_common_logging_dummy.$(OBJEXT)
+test_common_logging_dummy_OBJECTS = \
+ $(am_test_common_logging_dummy_OBJECTS)
+am_test_common_logging_runtime_loglevels_OBJECTS = \
+ test_common_logging_runtime_loglevels.$(OBJEXT)
+test_common_logging_runtime_loglevels_OBJECTS = \
+ $(am_test_common_logging_runtime_loglevels_OBJECTS)
+test_common_logging_runtime_loglevels_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_configuration_OBJECTS = test_configuration.$(OBJEXT)
+test_configuration_OBJECTS = $(am_test_configuration_OBJECTS)
+test_configuration_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_connection_OBJECTS = test_connection.$(OBJEXT)
+test_connection_OBJECTS = $(am_test_connection_OBJECTS)
+test_connection_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_connection_addressing_OBJECTS = \
+ test_connection_addressing.$(OBJEXT)
+test_connection_addressing_OBJECTS = \
+ $(am_test_connection_addressing_OBJECTS)
+test_connection_addressing_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_connection_receive_cancel_OBJECTS = \
+ test_connection_receive_cancel.$(OBJEXT)
+test_connection_receive_cancel_OBJECTS = \
+ $(am_test_connection_receive_cancel_OBJECTS)
+test_connection_receive_cancel_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_connection_timeout_OBJECTS = \
+ test_connection_timeout.$(OBJEXT)
+test_connection_timeout_OBJECTS = \
+ $(am_test_connection_timeout_OBJECTS)
+test_connection_timeout_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_connection_timeout_no_connect_OBJECTS = \
+ test_connection_timeout_no_connect.$(OBJEXT)
+test_connection_timeout_no_connect_OBJECTS = \
+ $(am_test_connection_timeout_no_connect_OBJECTS)
+test_connection_timeout_no_connect_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_connection_transmit_cancel_OBJECTS = \
+ test_connection_transmit_cancel.$(OBJEXT)
+test_connection_transmit_cancel_OBJECTS = \
+ $(am_test_connection_transmit_cancel_OBJECTS)
+test_connection_transmit_cancel_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_container_bloomfilter_OBJECTS = \
+ test_container_bloomfilter.$(OBJEXT)
+test_container_bloomfilter_OBJECTS = \
+ $(am_test_container_bloomfilter_OBJECTS)
+test_container_bloomfilter_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_container_heap_OBJECTS = test_container_heap.$(OBJEXT)
+test_container_heap_OBJECTS = $(am_test_container_heap_OBJECTS)
+test_container_heap_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_container_meta_data_OBJECTS = \
+ test_container_meta_data.$(OBJEXT)
+test_container_meta_data_OBJECTS = \
+ $(am_test_container_meta_data_OBJECTS)
+test_container_meta_data_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_container_multihashmap_OBJECTS = \
+ test_container_multihashmap.$(OBJEXT)
+test_container_multihashmap_OBJECTS = \
+ $(am_test_container_multihashmap_OBJECTS)
+test_container_multihashmap_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_container_slist_OBJECTS = test_container_slist.$(OBJEXT)
+test_container_slist_OBJECTS = $(am_test_container_slist_OBJECTS)
+test_container_slist_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_aes_OBJECTS = test_crypto_aes.$(OBJEXT)
+test_crypto_aes_OBJECTS = $(am_test_crypto_aes_OBJECTS)
+test_crypto_aes_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_aes_weak_OBJECTS = test_crypto_aes_weak.$(OBJEXT)
+test_crypto_aes_weak_OBJECTS = $(am_test_crypto_aes_weak_OBJECTS)
+test_crypto_aes_weak_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(am__DEPENDENCIES_1)
+am_test_crypto_crc_OBJECTS = test_crypto_crc.$(OBJEXT)
+test_crypto_crc_OBJECTS = $(am_test_crypto_crc_OBJECTS)
+test_crypto_crc_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_hash_OBJECTS = test_crypto_hash.$(OBJEXT)
+test_crypto_hash_OBJECTS = $(am_test_crypto_hash_OBJECTS)
+test_crypto_hash_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_hkdf_OBJECTS = test_crypto_hkdf.$(OBJEXT)
+test_crypto_hkdf_OBJECTS = $(am_test_crypto_hkdf_OBJECTS)
+test_crypto_hkdf_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_ksk_OBJECTS = test_crypto_ksk.$(OBJEXT)
+test_crypto_ksk_OBJECTS = $(am_test_crypto_ksk_OBJECTS)
+test_crypto_ksk_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_random_OBJECTS = test_crypto_random.$(OBJEXT)
+test_crypto_random_OBJECTS = $(am_test_crypto_random_OBJECTS)
+test_crypto_random_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_crypto_rsa_OBJECTS = test_crypto_rsa.$(OBJEXT)
+test_crypto_rsa_OBJECTS = $(am_test_crypto_rsa_OBJECTS)
+test_crypto_rsa_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_disk_OBJECTS = test_disk.$(OBJEXT)
+test_disk_OBJECTS = $(am_test_disk_OBJECTS)
+test_disk_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_getopt_OBJECTS = test_getopt.$(OBJEXT)
+test_getopt_OBJECTS = $(am_test_getopt_OBJECTS)
+test_getopt_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_os_network_OBJECTS = test_os_network.$(OBJEXT)
+test_os_network_OBJECTS = $(am_test_os_network_OBJECTS)
+test_os_network_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_os_priority_OBJECTS = test_os_priority.$(OBJEXT)
+test_os_priority_OBJECTS = $(am_test_os_priority_OBJECTS)
+test_os_priority_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_os_start_process_OBJECTS = test_os_start_process.$(OBJEXT)
+test_os_start_process_OBJECTS = $(am_test_os_start_process_OBJECTS)
+test_os_start_process_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_peer_OBJECTS = test_peer.$(OBJEXT)
+test_peer_OBJECTS = $(am_test_peer_OBJECTS)
+test_peer_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_plugin_OBJECTS = test_plugin.$(OBJEXT)
+test_plugin_OBJECTS = $(am_test_plugin_OBJECTS)
+test_plugin_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_program_OBJECTS = test_program.$(OBJEXT)
+test_program_OBJECTS = $(am_test_program_OBJECTS)
+test_program_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_pseudonym_OBJECTS = test_pseudonym.$(OBJEXT)
+test_pseudonym_OBJECTS = $(am_test_pseudonym_OBJECTS)
+test_pseudonym_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_resolver_api_OBJECTS = test_resolver_api.$(OBJEXT)
+test_resolver_api_OBJECTS = $(am_test_resolver_api_OBJECTS)
+test_resolver_api_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_scheduler_OBJECTS = test_scheduler.$(OBJEXT)
+test_scheduler_OBJECTS = $(am_test_scheduler_OBJECTS)
+test_scheduler_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_scheduler_delay_OBJECTS = test_scheduler_delay.$(OBJEXT)
+test_scheduler_delay_OBJECTS = $(am_test_scheduler_delay_OBJECTS)
+test_scheduler_delay_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_server_OBJECTS = test_server.$(OBJEXT)
+test_server_OBJECTS = $(am_test_server_OBJECTS)
+test_server_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_server_disconnect_OBJECTS = test_server_disconnect.$(OBJEXT)
+test_server_disconnect_OBJECTS = $(am_test_server_disconnect_OBJECTS)
+test_server_disconnect_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_server_with_client_OBJECTS = \
+ test_server_with_client.$(OBJEXT)
+test_server_with_client_OBJECTS = \
+ $(am_test_server_with_client_OBJECTS)
+test_server_with_client_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_server_with_client_unix_OBJECTS = \
+ test_server_with_client_unix.$(OBJEXT)
+test_server_with_client_unix_OBJECTS = \
+ $(am_test_server_with_client_unix_OBJECTS)
+test_server_with_client_unix_DEPENDENCIES = \
+ $(top_builddir)/src/util/libgnunetutil.la
+am_test_service_OBJECTS = test_service.$(OBJEXT)
+test_service_OBJECTS = $(am_test_service_OBJECTS)
+test_service_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_strings_OBJECTS = test_strings.$(OBJEXT)
+test_strings_OBJECTS = $(am_test_strings_OBJECTS)
+test_strings_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+am_test_time_OBJECTS = test_time.$(OBJEXT)
+test_time_OBJECTS = $(am_test_time_OBJECTS)
+test_time_DEPENDENCIES = $(top_builddir)/src/util/libgnunetutil.la
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__depfiles_maybe = depfiles
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_$(V))
+am__v_CC_ = $(am__v_CC_$(AM_DEFAULT_VERBOSITY))
+am__v_CC_0 = @echo " CC " $@;
+AM_V_at = $(am__v_at_$(V))
+am__v_at_ = $(am__v_at_$(AM_DEFAULT_VERBOSITY))
+am__v_at_0 = @
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_$(V))
+am__v_CCLD_ = $(am__v_CCLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CCLD_0 = @echo " CCLD " $@;
+CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
+ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS)
+LTCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CXX) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CXXFLAGS) $(CXXFLAGS)
+AM_V_CXX = $(am__v_CXX_$(V))
+am__v_CXX_ = $(am__v_CXX_$(AM_DEFAULT_VERBOSITY))
+am__v_CXX_0 = @echo " CXX " $@;
+CXXLD = $(CXX)
+CXXLINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(AM_CXXFLAGS) \
+ $(CXXFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CXXLD = $(am__v_CXXLD_$(V))
+am__v_CXXLD_ = $(am__v_CXXLD_$(AM_DEFAULT_VERBOSITY))
+am__v_CXXLD_0 = @echo " CXXLD " $@;
+AM_V_GEN = $(am__v_GEN_$(V))
+am__v_GEN_ = $(am__v_GEN_$(AM_DEFAULT_VERBOSITY))
+am__v_GEN_0 = @echo " GEN " $@;
+SOURCES = $(libgnunet_plugin_test_la_SOURCES) \
+ $(libgnunetutil_la_SOURCES) $(libgnunetutilwin_la_SOURCES) \
+ $(gnunet_config_diff_SOURCES) $(gnunet_resolver_SOURCES) \
+ $(gnunet_service_resolver_SOURCES) $(perf_crypto_hash_SOURCES) \
+ $(test_bio_SOURCES) $(test_client_SOURCES) \
+ $(test_common_allocation_SOURCES) \
+ $(test_common_endian_SOURCES) $(test_common_logging_SOURCES) \
+ $(test_common_logging_dummy_SOURCES) \
+ $(test_common_logging_runtime_loglevels_SOURCES) \
+ $(test_configuration_SOURCES) $(test_connection_SOURCES) \
+ $(test_connection_addressing_SOURCES) \
+ $(test_connection_receive_cancel_SOURCES) \
+ $(test_connection_timeout_SOURCES) \
+ $(test_connection_timeout_no_connect_SOURCES) \
+ $(test_connection_transmit_cancel_SOURCES) \
+ $(test_container_bloomfilter_SOURCES) \
+ $(test_container_heap_SOURCES) \
+ $(test_container_meta_data_SOURCES) \
+ $(test_container_multihashmap_SOURCES) \
+ $(test_container_slist_SOURCES) $(test_crypto_aes_SOURCES) \
+ $(test_crypto_aes_weak_SOURCES) $(test_crypto_crc_SOURCES) \
+ $(test_crypto_hash_SOURCES) $(test_crypto_hkdf_SOURCES) \
+ $(test_crypto_ksk_SOURCES) $(test_crypto_random_SOURCES) \
+ $(test_crypto_rsa_SOURCES) $(test_disk_SOURCES) \
+ $(test_getopt_SOURCES) $(test_os_network_SOURCES) \
+ $(test_os_priority_SOURCES) $(test_os_start_process_SOURCES) \
+ $(test_peer_SOURCES) $(test_plugin_SOURCES) \
+ $(test_program_SOURCES) $(test_pseudonym_SOURCES) \
+ $(test_resolver_api_SOURCES) $(test_scheduler_SOURCES) \
+ $(test_scheduler_delay_SOURCES) $(test_server_SOURCES) \
+ $(test_server_disconnect_SOURCES) \
+ $(test_server_with_client_SOURCES) \
+ $(test_server_with_client_unix_SOURCES) \
+ $(test_service_SOURCES) $(test_strings_SOURCES) \
+ $(test_time_SOURCES)
+DIST_SOURCES = $(libgnunet_plugin_test_la_SOURCES) \
+ $(libgnunetutil_la_SOURCES) \
+ $(am__libgnunetutilwin_la_SOURCES_DIST) \
+ $(gnunet_config_diff_SOURCES) $(gnunet_resolver_SOURCES) \
+ $(gnunet_service_resolver_SOURCES) $(perf_crypto_hash_SOURCES) \
+ $(test_bio_SOURCES) $(test_client_SOURCES) \
+ $(test_common_allocation_SOURCES) \
+ $(test_common_endian_SOURCES) $(test_common_logging_SOURCES) \
+ $(test_common_logging_dummy_SOURCES) \
+ $(test_common_logging_runtime_loglevels_SOURCES) \
+ $(test_configuration_SOURCES) $(test_connection_SOURCES) \
+ $(test_connection_addressing_SOURCES) \
+ $(test_connection_receive_cancel_SOURCES) \
+ $(test_connection_timeout_SOURCES) \
+ $(test_connection_timeout_no_connect_SOURCES) \
+ $(test_connection_transmit_cancel_SOURCES) \
+ $(test_container_bloomfilter_SOURCES) \
+ $(test_container_heap_SOURCES) \
+ $(test_container_meta_data_SOURCES) \
+ $(test_container_multihashmap_SOURCES) \
+ $(test_container_slist_SOURCES) $(test_crypto_aes_SOURCES) \
+ $(test_crypto_aes_weak_SOURCES) $(test_crypto_crc_SOURCES) \
+ $(test_crypto_hash_SOURCES) $(test_crypto_hkdf_SOURCES) \
+ $(test_crypto_ksk_SOURCES) $(test_crypto_random_SOURCES) \
+ $(test_crypto_rsa_SOURCES) $(test_disk_SOURCES) \
+ $(test_getopt_SOURCES) $(test_os_network_SOURCES) \
+ $(test_os_priority_SOURCES) $(test_os_start_process_SOURCES) \
+ $(test_peer_SOURCES) $(test_plugin_SOURCES) \
+ $(test_program_SOURCES) $(test_pseudonym_SOURCES) \
+ $(test_resolver_api_SOURCES) $(test_scheduler_SOURCES) \
+ $(test_scheduler_delay_SOURCES) $(test_server_SOURCES) \
+ $(test_server_disconnect_SOURCES) \
+ $(test_server_with_client_SOURCES) \
+ $(test_server_with_client_unix_SOURCES) \
+ $(test_service_SOURCES) $(test_strings_SOURCES) \
+ $(test_time_SOURCES)
+DATA = $(dist_pkgcfg_DATA) $(pkgcfg_DATA)
+ETAGS = etags
+CTAGS = ctags
+am__tty_colors = \
+red=; grn=; lgn=; blu=; std=
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+AMTAR = @AMTAR@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+ARGZ_H = @ARGZ_H@
+AS = @AS@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CYGPATH_W = @CYGPATH_W@
+DEFAULT_INTERFACE = @DEFAULT_INTERFACE@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLDIR = @DLLDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+EXEEXT = @EXEEXT@
+EXT_LIBS = @EXT_LIBS@
+EXT_LIB_PATH = @EXT_LIB_PATH@
+FGREP = @FGREP@
+GMSGFMT = @GMSGFMT@
+GMSGFMT_015 = @GMSGFMT_015@
+GNUNETDNS_GROUP = @GNUNETDNS_GROUP@
+GN_DAEMON_CONFIG_DIR = @GN_DAEMON_CONFIG_DIR@
+GN_DAEMON_HOME_DIR = @GN_DAEMON_HOME_DIR@
+GN_INTLINCL = @GN_INTLINCL@
+GN_LIBINTL = @GN_LIBINTL@
+GN_LIB_LDFLAGS = @GN_LIB_LDFLAGS@
+GN_PLUGIN_LDFLAGS = @GN_PLUGIN_LDFLAGS@
+GN_USER_HOME_DIR = @GN_USER_HOME_DIR@
+GREP = @GREP@
+HAVE_LIBUNISTRING = @HAVE_LIBUNISTRING@
+INCLTDL = @INCLTDL@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INTLLIBS = @INTLLIBS@
+INTL_MACOSX_LIBS = @INTL_MACOSX_LIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LIBADD_DL = @LIBADD_DL@
+LIBADD_DLD_LINK = @LIBADD_DLD_LINK@
+LIBADD_DLOPEN = @LIBADD_DLOPEN@
+LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBGCRYPT_CFLAGS = @LIBGCRYPT_CFLAGS@
+LIBGCRYPT_CONFIG = @LIBGCRYPT_CONFIG@
+LIBGCRYPT_LIBS = @LIBGCRYPT_LIBS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBLTDL = @LIBLTDL@
+LIBOBJS = @LIBOBJS@
+LIBPREFIX = @LIBPREFIX@
+LIBS = @LIBS@
+LIBTOOL = @LIBTOOL@
+LIBUNISTRING = @LIBUNISTRING@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTDLDEPS = @LTDLDEPS@
+LTDLINCL = @LTDLINCL@
+LTDLOPEN = @LTDLOPEN@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+LTLIBUNISTRING = @LTLIBUNISTRING@
+LT_CONFIG_H = @LT_CONFIG_H@
+LT_DLLOADERS = @LT_DLLOADERS@
+LT_DLPREOPEN = @LT_DLPREOPEN@
+MAKEINFO = @MAKEINFO@
+MKDIR_P = @MKDIR_P@
+MSGFMT = @MSGFMT@
+MSGFMT_015 = @MSGFMT_015@
+MSGMERGE = @MSGMERGE@
+MYSQL_CPPFLAGS = @MYSQL_CPPFLAGS@
+MYSQL_LDFLAGS = @MYSQL_LDFLAGS@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJC = @OBJC@
+OBJCDEPMODE = @OBJCDEPMODE@
+OBJCFLAGS = @OBJCFLAGS@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+POSTGRES_CPPFLAGS = @POSTGRES_CPPFLAGS@
+POSTGRES_LDFLAGS = @POSTGRES_LDFLAGS@
+POSUB = @POSUB@
+PYTHON = @PYTHON@
+PYTHON_EXEC_PREFIX = @PYTHON_EXEC_PREFIX@
+PYTHON_PLATFORM = @PYTHON_PLATFORM@
+PYTHON_PREFIX = @PYTHON_PREFIX@
+PYTHON_VERSION = @PYTHON_VERSION@
+RANLIB = @RANLIB@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+SQLITE_CPPFLAGS = @SQLITE_CPPFLAGS@
+SQLITE_LDFLAGS = @SQLITE_LDFLAGS@
+STRIP = @STRIP@
+SUDO_BINARY = @SUDO_BINARY@
+UNIXONLY = @UNIXONLY@
+USE_NLS = @USE_NLS@
+VERSION = @VERSION@
+XGETTEXT = @XGETTEXT@
+XGETTEXT_015 = @XGETTEXT_015@
+XMKMF = @XMKMF@
+X_CFLAGS = @X_CFLAGS@
+X_EXTRA_LIBS = @X_EXTRA_LIBS@
+X_LIBS = @X_LIBS@
+X_PRE_LIBS = @X_PRE_LIBS@
+_libcurl_config = @_libcurl_config@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+ac_ct_OBJC = @ac_ct_OBJC@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_target = @build_target@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+lt_ECHO = @lt_ECHO@
+ltdl_LIBOBJS = @ltdl_LIBOBJS@
+ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+pkgpyexecdir = @pkgpyexecdir@
+pkgpythondir = @pkgpythondir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+pyexecdir = @pyexecdir@
+pythondir = @pythondir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+subdirs = @subdirs@
+sys_symbol_underscore = @sys_symbol_underscore@
+sysconfdir = @sysconfdir@
+target = @target@
+target_alias = @target_alias@
+target_cpu = @target_cpu@
+target_os = @target_os@
+target_vendor = @target_vendor@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+INCLUDES = -I$(top_srcdir)/src/include -I$(top_builddir)/src/include
+plugindir = $(libdir)/gnunet
+pkgcfgdir = $(pkgdatadir)/config.d/
+dist_pkgcfg_DATA = \
+ util.conf
+
+pkgcfg_DATA = \
+ resolver.conf
+
+@MINGW_TRUE@noinst_LTLIBRARIES = \
+@MINGW_TRUE@ libgnunetutilwin.la
+
+@MINGW_TRUE@libgnunetutilwin_la_SOURCES = \
+@MINGW_TRUE@ win.cc \
+@MINGW_TRUE@ winproc.c
+
+@MINGW_TRUE@libgnunetutilwin_la_LDFLAGS = \
+@MINGW_TRUE@ -no-undefined -Wl,--export-all-symbols
+
+@MINGW_TRUE@libgnunetutilwin_la_LIBADD = \
+@MINGW_TRUE@ -lshell32 -liconv -lstdc++ \
+@MINGW_TRUE@ -lcomdlg32 -lgdi32 -liphlpapi
+
+@MINGW_TRUE@WINLIB = libgnunetutilwin.la
+@MINGW_FALSE@SERVER_CLIENT_UNIX = test_server_with_client_unix
+@USE_COVERAGE_TRUE@AM_CFLAGS = --coverage -O0
+@USE_COVERAGE_TRUE@XLIB = -lgcov
+gnunet_config_diff_SOURCES = \
+ gnunet-config-diff.c
+
+gnunet_config_diff_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+gnunet_config_diff_DEPENDENCIES = \
+ libgnunetutil.la
+
+test_common_logging_dummy_SOURCES = \
+ test_common_logging_dummy.c
+
+test_common_logging_dummy_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_common_logging_dummy_DEPENDENCIES = \
+ libgnunetutil.la
+
+lib_LTLIBRARIES = libgnunetutil.la
+libgnunetutil_la_SOURCES = \
+ bandwidth.c \
+ bio.c \
+ client.c \
+ common_allocation.c \
+ common_endian.c \
+ common_logging.c \
+ configuration.c \
+ connection.c \
+ container_bloomfilter.c \
+ container_heap.c \
+ container_meta_data.c \
+ container_multihashmap.c \
+ container_slist.c \
+ crypto_aes.c \
+ crypto_crc.c \
+ crypto_hash.c \
+ crypto_hkdf.c \
+ crypto_kdf.c \
+ crypto_ksk.c \
+ crypto_random.c \
+ crypto_rsa.c \
+ disk.c \
+ disk.h \
+ getopt.c \
+ getopt_helpers.c \
+ helper.c \
+ load.c \
+ network.c \
+ os_installation.c \
+ os_network.c \
+ os_priority.c \
+ peer.c \
+ plugin.c \
+ program.c \
+ pseudonym.c \
+ resolver_api.c resolver.h \
+ scheduler.c \
+ server.c \
+ server_mst.c \
+ server_nc.c \
+ server_tc.c \
+ service.c \
+ signal.c \
+ strings.c \
+ time.c
+
+libgnunetutil_la_LIBADD = \
+ $(GCLIBADD) $(WINLIB) \
+ $(LIBGCRYPT_LIBS) \
+ $(LTLIBICONV) \
+ -lltdl -lz $(XLIB)
+
+libgnunetutil_la_LDFLAGS = \
+ $(GN_LIB_LDFLAGS) \
+ -version-info 7:0:0
+
+gnunet_service_resolver_SOURCES = \
+ gnunet-service-resolver.c
+
+gnunet_service_resolver_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+
+gnunet_service_resolver_DEPENDENCIES = \
+ libgnunetutil.la
+
+gnunet_resolver_SOURCES = \
+ gnunet-resolver.c
+
+gnunet_resolver_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la \
+ $(GN_LIBINTL)
+
+gnunet_resolver_DEPENDENCIES = \
+ libgnunetutil.la
+
+plugin_LTLIBRARIES = \
+ libgnunet_plugin_test.la
+
+libgnunet_plugin_test_la_SOURCES = \
+ test_plugin_plug.c
+
+libgnunet_plugin_test_la_LDFLAGS = \
+ $(GN_PLUGIN_LDFLAGS)
+
+@HAVE_BENCHMARKS_TRUE@BENCHMARKS = \
+@HAVE_BENCHMARKS_TRUE@ perf_crypto_hash
+
+@ENABLE_TEST_RUN_TRUE@TESTS = $(check_PROGRAMS)
+test_bio_SOURCES = \
+ test_bio.c
+
+test_bio_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_os_start_process_SOURCES = \
+ test_os_start_process.c
+
+test_os_start_process_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+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_common_logging_runtime_loglevels_SOURCES = \
+ test_common_logging_runtime_loglevels.c
+
+test_common_logging_runtime_loglevels_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 -lextractor
+
+test_container_multihashmap_SOURCES = \
+ test_container_multihashmap.c
+
+test_container_multihashmap_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_container_heap_SOURCES = \
+ test_container_heap.c
+
+test_container_heap_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_container_slist_SOURCES = \
+ test_container_slist.c
+
+test_container_slist_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 \
+ $(LIBGCRYPT_LIBS)
+
+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_hkdf_SOURCES = \
+ test_crypto_hkdf.c
+
+test_crypto_hkdf_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_connection_SOURCES = \
+ test_connection.c
+
+test_connection_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_addressing_SOURCES = \
+ test_connection_addressing.c
+
+test_connection_addressing_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_receive_cancel_SOURCES = \
+ test_connection_receive_cancel.c
+
+test_connection_receive_cancel_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_timeout_SOURCES = \
+ test_connection_timeout.c
+
+test_connection_timeout_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_timeout_no_connect_SOURCES = \
+ test_connection_timeout_no_connect.c
+
+test_connection_timeout_no_connect_LDADD = \
+ $(top_builddir)/src/util/libgnunetutil.la
+
+test_connection_transmit_cancel_SOURCES = \
+ test_connection_transmit_cancel.c
+
+test_connection_transmit_cancel_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_peer_SOURCES = \
+ test_peer.c
+
+test_peer_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_resolver_api_SOURCES = \
+ test_resolver_api.c
+
+test_resolver_api_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_server_with_client_unix_SOURCES = \
+ test_server_with_client_unix.c
+
+test_server_with_client_unix_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_program_data.conf \
+ test_pseudonym_data.conf \
+ test_resolver_api_data.conf \
+ test_service_data.conf
+
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .cc .lo .o .obj
+$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu src/util/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu src/util/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+resolver.conf: $(top_builddir)/config.status $(srcdir)/resolver.conf.in
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@
+install-libLTLIBRARIES: $(lib_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(libdir)" || $(MKDIR_P) "$(DESTDIR)$(libdir)"
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(libdir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(libdir)"; \
+ }
+
+uninstall-libLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(lib_LTLIBRARIES)'; test -n "$(libdir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(libdir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(libdir)/$$f"; \
+ done
+
+clean-libLTLIBRARIES:
+ -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES)
+ @list='$(lib_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+
+clean-noinstLTLIBRARIES:
+ -test -z "$(noinst_LTLIBRARIES)" || rm -f $(noinst_LTLIBRARIES)
+ @list='$(noinst_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+install-pluginLTLIBRARIES: $(plugin_LTLIBRARIES)
+ @$(NORMAL_INSTALL)
+ test -z "$(plugindir)" || $(MKDIR_P) "$(DESTDIR)$(plugindir)"
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ list2=; for p in $$list; do \
+ if test -f $$p; then \
+ list2="$$list2 $$p"; \
+ else :; fi; \
+ done; \
+ test -z "$$list2" || { \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 '$(DESTDIR)$(plugindir)'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL) $(INSTALL_STRIP_FLAG) $$list2 "$(DESTDIR)$(plugindir)"; \
+ }
+
+uninstall-pluginLTLIBRARIES:
+ @$(NORMAL_UNINSTALL)
+ @list='$(plugin_LTLIBRARIES)'; test -n "$(plugindir)" || list=; \
+ for p in $$list; do \
+ $(am__strip_dir) \
+ echo " $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f '$(DESTDIR)$(plugindir)/$$f'"; \
+ $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=uninstall rm -f "$(DESTDIR)$(plugindir)/$$f"; \
+ done
+
+clean-pluginLTLIBRARIES:
+ -test -z "$(plugin_LTLIBRARIES)" || rm -f $(plugin_LTLIBRARIES)
+ @list='$(plugin_LTLIBRARIES)'; for p in $$list; do \
+ dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \
+ test "$$dir" != "$$p" || dir=.; \
+ echo "rm -f \"$${dir}/so_locations\""; \
+ rm -f "$${dir}/so_locations"; \
+ done
+libgnunet_plugin_test.la: $(libgnunet_plugin_test_la_OBJECTS) $(libgnunet_plugin_test_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgnunet_plugin_test_la_LINK) -rpath $(plugindir) $(libgnunet_plugin_test_la_OBJECTS) $(libgnunet_plugin_test_la_LIBADD) $(LIBS)
+libgnunetutil.la: $(libgnunetutil_la_OBJECTS) $(libgnunetutil_la_DEPENDENCIES)
+ $(AM_V_CCLD)$(libgnunetutil_la_LINK) -rpath $(libdir) $(libgnunetutil_la_OBJECTS) $(libgnunetutil_la_LIBADD) $(LIBS)
+libgnunetutilwin.la: $(libgnunetutilwin_la_OBJECTS) $(libgnunetutilwin_la_DEPENDENCIES)
+ $(AM_V_CXXLD)$(libgnunetutilwin_la_LINK) $(am_libgnunetutilwin_la_rpath) $(libgnunetutilwin_la_OBJECTS) $(libgnunetutilwin_la_LIBADD) $(LIBS)
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p || test -f $$p1; \
+ then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(bindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(bindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; test -n "$(bindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(bindir)" && rm -f $$files
+
+clean-binPROGRAMS:
+ @list='$(bin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-checkPROGRAMS:
+ @list='$(check_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+clean-noinstPROGRAMS:
+ @list='$(noinst_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+gnunet-config-diff$(EXEEXT): $(gnunet_config_diff_OBJECTS) $(gnunet_config_diff_DEPENDENCIES)
+ @rm -f gnunet-config-diff$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_config_diff_OBJECTS) $(gnunet_config_diff_LDADD) $(LIBS)
+gnunet-resolver$(EXEEXT): $(gnunet_resolver_OBJECTS) $(gnunet_resolver_DEPENDENCIES)
+ @rm -f gnunet-resolver$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_resolver_OBJECTS) $(gnunet_resolver_LDADD) $(LIBS)
+gnunet-service-resolver$(EXEEXT): $(gnunet_service_resolver_OBJECTS) $(gnunet_service_resolver_DEPENDENCIES)
+ @rm -f gnunet-service-resolver$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(gnunet_service_resolver_OBJECTS) $(gnunet_service_resolver_LDADD) $(LIBS)
+perf_crypto_hash$(EXEEXT): $(perf_crypto_hash_OBJECTS) $(perf_crypto_hash_DEPENDENCIES)
+ @rm -f perf_crypto_hash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(perf_crypto_hash_OBJECTS) $(perf_crypto_hash_LDADD) $(LIBS)
+test_bio$(EXEEXT): $(test_bio_OBJECTS) $(test_bio_DEPENDENCIES)
+ @rm -f test_bio$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_bio_OBJECTS) $(test_bio_LDADD) $(LIBS)
+test_client$(EXEEXT): $(test_client_OBJECTS) $(test_client_DEPENDENCIES)
+ @rm -f test_client$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_client_OBJECTS) $(test_client_LDADD) $(LIBS)
+test_common_allocation$(EXEEXT): $(test_common_allocation_OBJECTS) $(test_common_allocation_DEPENDENCIES)
+ @rm -f test_common_allocation$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_common_allocation_OBJECTS) $(test_common_allocation_LDADD) $(LIBS)
+test_common_endian$(EXEEXT): $(test_common_endian_OBJECTS) $(test_common_endian_DEPENDENCIES)
+ @rm -f test_common_endian$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_common_endian_OBJECTS) $(test_common_endian_LDADD) $(LIBS)
+test_common_logging$(EXEEXT): $(test_common_logging_OBJECTS) $(test_common_logging_DEPENDENCIES)
+ @rm -f test_common_logging$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_common_logging_OBJECTS) $(test_common_logging_LDADD) $(LIBS)
+test_common_logging_dummy$(EXEEXT): $(test_common_logging_dummy_OBJECTS) $(test_common_logging_dummy_DEPENDENCIES)
+ @rm -f test_common_logging_dummy$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_common_logging_dummy_OBJECTS) $(test_common_logging_dummy_LDADD) $(LIBS)
+test_common_logging_runtime_loglevels$(EXEEXT): $(test_common_logging_runtime_loglevels_OBJECTS) $(test_common_logging_runtime_loglevels_DEPENDENCIES)
+ @rm -f test_common_logging_runtime_loglevels$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_common_logging_runtime_loglevels_OBJECTS) $(test_common_logging_runtime_loglevels_LDADD) $(LIBS)
+test_configuration$(EXEEXT): $(test_configuration_OBJECTS) $(test_configuration_DEPENDENCIES)
+ @rm -f test_configuration$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_configuration_OBJECTS) $(test_configuration_LDADD) $(LIBS)
+test_connection$(EXEEXT): $(test_connection_OBJECTS) $(test_connection_DEPENDENCIES)
+ @rm -f test_connection$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_connection_OBJECTS) $(test_connection_LDADD) $(LIBS)
+test_connection_addressing$(EXEEXT): $(test_connection_addressing_OBJECTS) $(test_connection_addressing_DEPENDENCIES)
+ @rm -f test_connection_addressing$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_connection_addressing_OBJECTS) $(test_connection_addressing_LDADD) $(LIBS)
+test_connection_receive_cancel$(EXEEXT): $(test_connection_receive_cancel_OBJECTS) $(test_connection_receive_cancel_DEPENDENCIES)
+ @rm -f test_connection_receive_cancel$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_connection_receive_cancel_OBJECTS) $(test_connection_receive_cancel_LDADD) $(LIBS)
+test_connection_timeout$(EXEEXT): $(test_connection_timeout_OBJECTS) $(test_connection_timeout_DEPENDENCIES)
+ @rm -f test_connection_timeout$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_connection_timeout_OBJECTS) $(test_connection_timeout_LDADD) $(LIBS)
+test_connection_timeout_no_connect$(EXEEXT): $(test_connection_timeout_no_connect_OBJECTS) $(test_connection_timeout_no_connect_DEPENDENCIES)
+ @rm -f test_connection_timeout_no_connect$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_connection_timeout_no_connect_OBJECTS) $(test_connection_timeout_no_connect_LDADD) $(LIBS)
+test_connection_transmit_cancel$(EXEEXT): $(test_connection_transmit_cancel_OBJECTS) $(test_connection_transmit_cancel_DEPENDENCIES)
+ @rm -f test_connection_transmit_cancel$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_connection_transmit_cancel_OBJECTS) $(test_connection_transmit_cancel_LDADD) $(LIBS)
+test_container_bloomfilter$(EXEEXT): $(test_container_bloomfilter_OBJECTS) $(test_container_bloomfilter_DEPENDENCIES)
+ @rm -f test_container_bloomfilter$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_container_bloomfilter_OBJECTS) $(test_container_bloomfilter_LDADD) $(LIBS)
+test_container_heap$(EXEEXT): $(test_container_heap_OBJECTS) $(test_container_heap_DEPENDENCIES)
+ @rm -f test_container_heap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_container_heap_OBJECTS) $(test_container_heap_LDADD) $(LIBS)
+test_container_meta_data$(EXEEXT): $(test_container_meta_data_OBJECTS) $(test_container_meta_data_DEPENDENCIES)
+ @rm -f test_container_meta_data$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_container_meta_data_OBJECTS) $(test_container_meta_data_LDADD) $(LIBS)
+test_container_multihashmap$(EXEEXT): $(test_container_multihashmap_OBJECTS) $(test_container_multihashmap_DEPENDENCIES)
+ @rm -f test_container_multihashmap$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_container_multihashmap_OBJECTS) $(test_container_multihashmap_LDADD) $(LIBS)
+test_container_slist$(EXEEXT): $(test_container_slist_OBJECTS) $(test_container_slist_DEPENDENCIES)
+ @rm -f test_container_slist$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_container_slist_OBJECTS) $(test_container_slist_LDADD) $(LIBS)
+test_crypto_aes$(EXEEXT): $(test_crypto_aes_OBJECTS) $(test_crypto_aes_DEPENDENCIES)
+ @rm -f test_crypto_aes$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_aes_OBJECTS) $(test_crypto_aes_LDADD) $(LIBS)
+test_crypto_aes_weak$(EXEEXT): $(test_crypto_aes_weak_OBJECTS) $(test_crypto_aes_weak_DEPENDENCIES)
+ @rm -f test_crypto_aes_weak$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_aes_weak_OBJECTS) $(test_crypto_aes_weak_LDADD) $(LIBS)
+test_crypto_crc$(EXEEXT): $(test_crypto_crc_OBJECTS) $(test_crypto_crc_DEPENDENCIES)
+ @rm -f test_crypto_crc$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_crc_OBJECTS) $(test_crypto_crc_LDADD) $(LIBS)
+test_crypto_hash$(EXEEXT): $(test_crypto_hash_OBJECTS) $(test_crypto_hash_DEPENDENCIES)
+ @rm -f test_crypto_hash$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_hash_OBJECTS) $(test_crypto_hash_LDADD) $(LIBS)
+test_crypto_hkdf$(EXEEXT): $(test_crypto_hkdf_OBJECTS) $(test_crypto_hkdf_DEPENDENCIES)
+ @rm -f test_crypto_hkdf$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_hkdf_OBJECTS) $(test_crypto_hkdf_LDADD) $(LIBS)
+test_crypto_ksk$(EXEEXT): $(test_crypto_ksk_OBJECTS) $(test_crypto_ksk_DEPENDENCIES)
+ @rm -f test_crypto_ksk$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_ksk_OBJECTS) $(test_crypto_ksk_LDADD) $(LIBS)
+test_crypto_random$(EXEEXT): $(test_crypto_random_OBJECTS) $(test_crypto_random_DEPENDENCIES)
+ @rm -f test_crypto_random$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_random_OBJECTS) $(test_crypto_random_LDADD) $(LIBS)
+test_crypto_rsa$(EXEEXT): $(test_crypto_rsa_OBJECTS) $(test_crypto_rsa_DEPENDENCIES)
+ @rm -f test_crypto_rsa$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_crypto_rsa_OBJECTS) $(test_crypto_rsa_LDADD) $(LIBS)
+test_disk$(EXEEXT): $(test_disk_OBJECTS) $(test_disk_DEPENDENCIES)
+ @rm -f test_disk$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_disk_OBJECTS) $(test_disk_LDADD) $(LIBS)
+test_getopt$(EXEEXT): $(test_getopt_OBJECTS) $(test_getopt_DEPENDENCIES)
+ @rm -f test_getopt$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_getopt_OBJECTS) $(test_getopt_LDADD) $(LIBS)
+test_os_network$(EXEEXT): $(test_os_network_OBJECTS) $(test_os_network_DEPENDENCIES)
+ @rm -f test_os_network$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_os_network_OBJECTS) $(test_os_network_LDADD) $(LIBS)
+test_os_priority$(EXEEXT): $(test_os_priority_OBJECTS) $(test_os_priority_DEPENDENCIES)
+ @rm -f test_os_priority$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_os_priority_OBJECTS) $(test_os_priority_LDADD) $(LIBS)
+test_os_start_process$(EXEEXT): $(test_os_start_process_OBJECTS) $(test_os_start_process_DEPENDENCIES)
+ @rm -f test_os_start_process$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_os_start_process_OBJECTS) $(test_os_start_process_LDADD) $(LIBS)
+test_peer$(EXEEXT): $(test_peer_OBJECTS) $(test_peer_DEPENDENCIES)
+ @rm -f test_peer$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_peer_OBJECTS) $(test_peer_LDADD) $(LIBS)
+test_plugin$(EXEEXT): $(test_plugin_OBJECTS) $(test_plugin_DEPENDENCIES)
+ @rm -f test_plugin$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_plugin_OBJECTS) $(test_plugin_LDADD) $(LIBS)
+test_program$(EXEEXT): $(test_program_OBJECTS) $(test_program_DEPENDENCIES)
+ @rm -f test_program$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_program_OBJECTS) $(test_program_LDADD) $(LIBS)
+test_pseudonym$(EXEEXT): $(test_pseudonym_OBJECTS) $(test_pseudonym_DEPENDENCIES)
+ @rm -f test_pseudonym$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_pseudonym_OBJECTS) $(test_pseudonym_LDADD) $(LIBS)
+test_resolver_api$(EXEEXT): $(test_resolver_api_OBJECTS) $(test_resolver_api_DEPENDENCIES)
+ @rm -f test_resolver_api$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_resolver_api_OBJECTS) $(test_resolver_api_LDADD) $(LIBS)
+test_scheduler$(EXEEXT): $(test_scheduler_OBJECTS) $(test_scheduler_DEPENDENCIES)
+ @rm -f test_scheduler$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_scheduler_OBJECTS) $(test_scheduler_LDADD) $(LIBS)
+test_scheduler_delay$(EXEEXT): $(test_scheduler_delay_OBJECTS) $(test_scheduler_delay_DEPENDENCIES)
+ @rm -f test_scheduler_delay$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_scheduler_delay_OBJECTS) $(test_scheduler_delay_LDADD) $(LIBS)
+test_server$(EXEEXT): $(test_server_OBJECTS) $(test_server_DEPENDENCIES)
+ @rm -f test_server$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_server_OBJECTS) $(test_server_LDADD) $(LIBS)
+test_server_disconnect$(EXEEXT): $(test_server_disconnect_OBJECTS) $(test_server_disconnect_DEPENDENCIES)
+ @rm -f test_server_disconnect$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_server_disconnect_OBJECTS) $(test_server_disconnect_LDADD) $(LIBS)
+test_server_with_client$(EXEEXT): $(test_server_with_client_OBJECTS) $(test_server_with_client_DEPENDENCIES)
+ @rm -f test_server_with_client$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_server_with_client_OBJECTS) $(test_server_with_client_LDADD) $(LIBS)
+test_server_with_client_unix$(EXEEXT): $(test_server_with_client_unix_OBJECTS) $(test_server_with_client_unix_DEPENDENCIES)
+ @rm -f test_server_with_client_unix$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_server_with_client_unix_OBJECTS) $(test_server_with_client_unix_LDADD) $(LIBS)
+test_service$(EXEEXT): $(test_service_OBJECTS) $(test_service_DEPENDENCIES)
+ @rm -f test_service$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_service_OBJECTS) $(test_service_LDADD) $(LIBS)
+test_strings$(EXEEXT): $(test_strings_OBJECTS) $(test_strings_DEPENDENCIES)
+ @rm -f test_strings$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_strings_OBJECTS) $(test_strings_LDADD) $(LIBS)
+test_time$(EXEEXT): $(test_time_OBJECTS) $(test_time_DEPENDENCIES)
+ @rm -f test_time$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(test_time_OBJECTS) $(test_time_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bandwidth.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bio.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/client.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common_allocation.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common_endian.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/common_logging.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configuration.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/connection.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/container_bloomfilter.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/container_heap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/container_meta_data.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/container_multihashmap.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/container_slist.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_aes.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_crc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_hash.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_hkdf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_kdf.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_ksk.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_random.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/crypto_rsa.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/disk.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getopt_helpers.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-config-diff.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-resolver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gnunet-service-resolver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helper.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/load.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_installation.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_network.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/os_priority.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/peer.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/perf_crypto_hash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plugin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/program.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pseudonym.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/resolver_api.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/scheduler.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_mst.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_nc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/server_tc.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/service.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/strings.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_bio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_common_allocation.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_common_endian.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_common_logging.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_common_logging_dummy.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_common_logging_runtime_loglevels.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_configuration.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_connection.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_connection_addressing.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_connection_receive_cancel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_connection_timeout.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_connection_timeout_no_connect.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_connection_transmit_cancel.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_container_bloomfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_container_heap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_container_meta_data.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_container_multihashmap.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_container_slist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_aes.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_aes_weak.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_crc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_hash.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_hkdf.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_ksk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_random.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_crypto_rsa.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_disk.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_getopt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_os_network.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_os_priority.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_os_start_process.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_peer.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_plugin.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_plugin_plug.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_program.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_pseudonym.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_resolver_api.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_scheduler.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_scheduler_delay.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_server.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_server_disconnect.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_server_with_client.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_server_with_client_unix.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_service.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_strings.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/test_time.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/time.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/win.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/winproc.Plo@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCC_FALSE@ $(AM_V_CC) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(LTCOMPILE) -c -o $@ $<
+
+.cc.o:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
+
+.cc.obj:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.cc.lo:
+@am__fastdepCXX_TRUE@ $(AM_V_CXX)$(LTCXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCXX_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@am__fastdepCXX_FALSE@ $(AM_V_CXX) @AM_BACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCXX_FALSE@ $(LTCXXCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-dist_pkgcfgDATA: $(dist_pkgcfg_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)"
+ @list='$(dist_pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgcfgdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgcfgdir)" || exit $$?; \
+ done
+
+uninstall-dist_pkgcfgDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(dist_pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkgcfgdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkgcfgdir)" && rm -f $$files
+install-pkgcfgDATA: $(pkgcfg_DATA)
+ @$(NORMAL_INSTALL)
+ test -z "$(pkgcfgdir)" || $(MKDIR_P) "$(DESTDIR)$(pkgcfgdir)"
+ @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ for p in $$list; do \
+ if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; \
+ done | $(am__base_list) | \
+ while read files; do \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(pkgcfgdir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(pkgcfgdir)" || exit $$?; \
+ done
+
+uninstall-pkgcfgDATA:
+ @$(NORMAL_UNINSTALL)
+ @list='$(pkgcfg_DATA)'; test -n "$(pkgcfgdir)" || list=; \
+ files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
+ test -n "$$files" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(pkgcfgdir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(pkgcfgdir)" && rm -f $$files
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ set x; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) '{ files[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in files) print i; }; }'`; \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+check-TESTS: $(TESTS)
+ @failed=0; all=0; xfail=0; xpass=0; skip=0; \
+ srcdir=$(srcdir); export srcdir; \
+ list=' $(TESTS) '; \
+ $(am__tty_colors); \
+ if test -n "$$list"; then \
+ for tst in $$list; do \
+ if test -f ./$$tst; then dir=./; \
+ elif test -f $$tst; then dir=; \
+ else dir="$(srcdir)/"; fi; \
+ if $(TESTS_ENVIRONMENT) $${dir}$$tst; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xpass=`expr $$xpass + 1`; \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=XPASS; \
+ ;; \
+ *) \
+ col=$$grn; res=PASS; \
+ ;; \
+ esac; \
+ elif test $$? -ne 77; then \
+ all=`expr $$all + 1`; \
+ case " $(XFAIL_TESTS) " in \
+ *[\ \ ]$$tst[\ \ ]*) \
+ xfail=`expr $$xfail + 1`; \
+ col=$$lgn; res=XFAIL; \
+ ;; \
+ *) \
+ failed=`expr $$failed + 1`; \
+ col=$$red; res=FAIL; \
+ ;; \
+ esac; \
+ else \
+ skip=`expr $$skip + 1`; \
+ col=$$blu; res=SKIP; \
+ fi; \
+ echo "$${col}$$res$${std}: $$tst"; \
+ done; \
+ if test "$$all" -eq 1; then \
+ tests="test"; \
+ All=""; \
+ else \
+ tests="tests"; \
+ All="All "; \
+ fi; \
+ if test "$$failed" -eq 0; then \
+ if test "$$xfail" -eq 0; then \
+ banner="$$All$$all $$tests passed"; \
+ else \
+ if test "$$xfail" -eq 1; then failures=failure; else failures=failures; fi; \
+ banner="$$All$$all $$tests behaved as expected ($$xfail expected $$failures)"; \
+ fi; \
+ else \
+ if test "$$xpass" -eq 0; then \
+ banner="$$failed of $$all $$tests failed"; \
+ else \
+ if test "$$xpass" -eq 1; then passes=pass; else passes=passes; fi; \
+ banner="$$failed of $$all $$tests did not behave as expected ($$xpass unexpected $$passes)"; \
+ fi; \
+ fi; \
+ dashes="$$banner"; \
+ skipped=""; \
+ if test "$$skip" -ne 0; then \
+ if test "$$skip" -eq 1; then \
+ skipped="($$skip test was not run)"; \
+ else \
+ skipped="($$skip tests were not run)"; \
+ fi; \
+ test `echo "$$skipped" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$skipped"; \
+ fi; \
+ report=""; \
+ if test "$$failed" -ne 0 && test -n "$(PACKAGE_BUGREPORT)"; then \
+ report="Please report to $(PACKAGE_BUGREPORT)"; \
+ test `echo "$$report" | wc -c` -le `echo "$$banner" | wc -c` || \
+ dashes="$$report"; \
+ fi; \
+ dashes=`echo "$$dashes" | sed s/./=/g`; \
+ if test "$$failed" -eq 0; then \
+ echo "$$grn$$dashes"; \
+ else \
+ echo "$$red$$dashes"; \
+ fi; \
+ echo "$$banner"; \
+ test -z "$$skipped" || echo "$$skipped"; \
+ test -z "$$report" || echo "$$report"; \
+ echo "$$dashes$$std"; \
+ test "$$failed" -eq 0; \
+ else :; fi
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+ $(MAKE) $(AM_MAKEFLAGS) $(check_PROGRAMS)
+ $(MAKE) $(AM_MAKEFLAGS) check-TESTS
+check: check-am
+all-am: Makefile $(LTLIBRARIES) $(PROGRAMS) $(DATA)
+install-binPROGRAMS: install-libLTLIBRARIES
+
+installdirs:
+ for dir in "$(DESTDIR)$(libdir)" "$(DESTDIR)$(plugindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(pkgcfgdir)" "$(DESTDIR)$(pkgcfgdir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \
+ clean-noinstPROGRAMS clean-pluginLTLIBRARIES mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-dist_pkgcfgDATA install-pkgcfgDATA \
+ install-pluginLTLIBRARIES
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-binPROGRAMS install-libLTLIBRARIES
+
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man:
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-dist_pkgcfgDATA \
+ uninstall-libLTLIBRARIES uninstall-pkgcfgDATA \
+ uninstall-pluginLTLIBRARIES
+
+.MAKE: check-am install-am install-strip
+
+.PHONY: CTAGS GTAGS all all-am check check-TESTS check-am clean \
+ clean-binPROGRAMS clean-checkPROGRAMS clean-generic \
+ clean-libLTLIBRARIES clean-libtool clean-noinstLTLIBRARIES \
+ clean-noinstPROGRAMS clean-pluginLTLIBRARIES ctags distclean \
+ distclean-compile distclean-generic distclean-libtool \
+ distclean-tags distdir dvi dvi-am html html-am info info-am \
+ install install-am install-binPROGRAMS install-data \
+ install-data-am install-dist_pkgcfgDATA install-dvi \
+ install-dvi-am install-exec install-exec-am install-html \
+ install-html-am install-info install-info-am \
+ install-libLTLIBRARIES install-man install-pdf install-pdf-am \
+ install-pkgcfgDATA install-pluginLTLIBRARIES install-ps \
+ install-ps-am install-strip installcheck installcheck-am \
+ installdirs maintainer-clean maintainer-clean-generic \
+ mostlyclean mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-dist_pkgcfgDATA \
+ uninstall-libLTLIBRARIES uninstall-pkgcfgDATA \
+ uninstall-pluginLTLIBRARIES
+
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/src/util/bandwidth.c b/src/util/bandwidth.c
new file mode 100644
index 0000000..9d67949
--- /dev/null
+++ b/src/util/bandwidth.c
@@ -0,0 +1,328 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010 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/bandwidth.c
+ * @brief functions related to bandwidth (unit)
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_bandwidth_lib.h"
+#include "gnunet_server_lib.h"
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util-bandwidth", __VA_ARGS__)
+
+/**
+ * Create a new bandwidth value.
+ *
+ * @param bytes_per_second value to create
+ * @return the new bandwidth value
+ */
+struct GNUNET_BANDWIDTH_Value32NBO
+GNUNET_BANDWIDTH_value_init (uint32_t bytes_per_second)
+{
+ struct GNUNET_BANDWIDTH_Value32NBO ret;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Initializing bandwidth of %u Bps\n",
+ (unsigned int) bytes_per_second);
+ ret.value__ = htonl (bytes_per_second);
+ return ret;
+}
+
+
+/**
+ * Compute the MIN of two bandwidth values.
+ *
+ * @param b1 first value
+ * @param b2 second value
+ * @return the min of b1 and b2
+ */
+struct GNUNET_BANDWIDTH_Value32NBO
+GNUNET_BANDWIDTH_value_min (struct GNUNET_BANDWIDTH_Value32NBO b1,
+ struct GNUNET_BANDWIDTH_Value32NBO b2)
+{
+ return
+ GNUNET_BANDWIDTH_value_init (GNUNET_MIN
+ (ntohl (b1.value__), ntohl (b2.value__)));
+}
+
+
+/**
+ * At the given bandwidth, calculate how much traffic will be
+ * available until the given deadline.
+ *
+ * @param bps bandwidth
+ * @param deadline when is the deadline
+ * @return number of bytes available at bps until deadline
+ */
+uint64_t
+GNUNET_BANDWIDTH_value_get_available_until (struct GNUNET_BANDWIDTH_Value32NBO
+ bps,
+ struct GNUNET_TIME_Relative
+ deadline)
+{
+ uint64_t b;
+
+ b = ntohl (bps.value__);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Bandwidth has %llu bytes available until deadline in %llums\n",
+ (unsigned long long) ((b * deadline.rel_value + 500LL) / 1000LL),
+ deadline.rel_value);
+ return (b * deadline.rel_value + 500LL) / 1000LL;
+}
+
+
+/**
+ * At the given bandwidth, calculate how long it would take for
+ * 'size' bytes to be transmitted.
+ *
+ * @param bps bandwidth
+ * @param size number of bytes we want to have available
+ * @return how long it would take
+ */
+struct GNUNET_TIME_Relative
+GNUNET_BANDWIDTH_value_get_delay_for (struct GNUNET_BANDWIDTH_Value32NBO bps,
+ uint64_t size)
+{
+ uint64_t b;
+ struct GNUNET_TIME_Relative ret;
+
+ b = ntohl (bps.value__);
+ if (b == 0)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Bandwidth suggests delay of infinity (zero bandwidth)\n");
+ return GNUNET_TIME_UNIT_FOREVER_REL;
+ }
+ ret.rel_value = size * 1000LL / b;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Bandwidth suggests delay of %llu ms for %llu bytes of traffic\n",
+ (unsigned long long) ret.rel_value, (unsigned long long) size);
+ return ret;
+}
+
+
+/**
+ * Initialize bandwidth tracker. Note that in addition to the
+ * 'max_carry_s' limit, we also always allow at least
+ * GNUNET_SERVER_MAX_MESSAGE_SIZE to accumulate. So if the
+ * bytes-per-second limit is so small that within 'max_carry_s' not
+ * even GNUNET_SERVER_MAX_MESSAGE_SIZE is allowed to accumulate, it is
+ * ignored and replaced by GNUNET_SERVER_MAX_MESSAGE_SIZE (which is in
+ * bytes).
+ *
+ * @param av tracker to initialize
+ * @param bytes_per_second_limit initial limit to assume
+ * @param max_carry_s maximum number of seconds unused bandwidth
+ * may accumulate before it expires
+ */
+void
+GNUNET_BANDWIDTH_tracker_init (struct GNUNET_BANDWIDTH_Tracker *av,
+ struct GNUNET_BANDWIDTH_Value32NBO
+ bytes_per_second_limit, uint32_t max_carry_s)
+{
+ av->consumption_since_last_update__ = 0;
+ av->last_update__ = GNUNET_TIME_absolute_get ();
+ av->available_bytes_per_s__ = ntohl (bytes_per_second_limit.value__);
+ av->max_carry_s__ = max_carry_s;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tracker %p initialized with %u Bps and max carry %u\n", av,
+ (unsigned int) av->available_bytes_per_s__, (unsigned int) max_carry_s);
+}
+
+
+/**
+ * Update the tracker, looking at the current time and
+ * bandwidth consumption data.
+ *
+ * @param av tracker to update
+ */
+static void
+update_tracker (struct GNUNET_BANDWIDTH_Tracker *av)
+{
+ struct GNUNET_TIME_Absolute now;
+ uint64_t delta_time;
+ uint64_t delta_avail;
+ uint64_t left_bytes;
+ uint64_t max_carry;
+
+ now = GNUNET_TIME_absolute_get ();
+ delta_time = now.abs_value - av->last_update__.abs_value;
+ delta_avail =
+ (delta_time * ((unsigned long long) av->available_bytes_per_s__) +
+ 500LL) / 1000LL;
+ av->consumption_since_last_update__ -= delta_avail;
+ av->last_update__ = now;
+ if (av->consumption_since_last_update__ < 0)
+ {
+ left_bytes = -av->consumption_since_last_update__;
+ max_carry = av->available_bytes_per_s__ * av->max_carry_s__;
+ if (max_carry < GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ max_carry = GNUNET_SERVER_MAX_MESSAGE_SIZE;
+ if (max_carry > left_bytes)
+ av->consumption_since_last_update__ = -left_bytes;
+ else
+ av->consumption_since_last_update__ = -max_carry;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tracker %p updated, have %u Bps, last update was %llu ms ago\n", av,
+ (unsigned int) av->available_bytes_per_s__,
+ (unsigned long long) delta_time);
+}
+
+
+/**
+ * Notify the tracker that a certain number of bytes of bandwidth have
+ * been consumed. Note that it is legal to consume bytes even if not
+ * enough bandwidth is available (in that case,
+ * GNUNET_BANDWIDTH_tracker_get_delay may return non-zero delay values
+ * even for a size of zero for a while).
+ *
+ * @param av tracker to update
+ * @param size number of bytes consumed
+ * @return GNUNET_YES if this consumption is above the limit
+ */
+int
+GNUNET_BANDWIDTH_tracker_consume (struct GNUNET_BANDWIDTH_Tracker *av,
+ ssize_t size)
+{
+ int64_t nc;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Tracker %p consumes %d bytes\n", av,
+ (int) size);
+ if (size > 0)
+ {
+ nc = av->consumption_since_last_update__ + size;
+ if (nc < av->consumption_since_last_update__)
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ av->consumption_since_last_update__ = nc;
+ update_tracker (av);
+ if (av->consumption_since_last_update__ > 0)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tracker %p consumption %llu bytes above limit\n", av,
+ (unsigned long long) av->consumption_since_last_update__);
+ return GNUNET_YES;
+ }
+ }
+ else
+ {
+ av->consumption_since_last_update__ += size;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Compute how long we should wait until consuming 'size'
+ * bytes of bandwidth in order to stay within the given
+ * quota.
+ *
+ * @param av tracker to query
+ * @param size number of bytes we would like to consume
+ * @return time in ms to wait for consumption to be OK
+ */
+struct GNUNET_TIME_Relative
+GNUNET_BANDWIDTH_tracker_get_delay (struct GNUNET_BANDWIDTH_Tracker *av,
+ size_t size)
+{
+ struct GNUNET_TIME_Relative ret;
+ int64_t bytes_needed;
+
+ if (av->available_bytes_per_s__ == 0)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Tracker %p delay is infinity\n", av);
+ return GNUNET_TIME_UNIT_FOREVER_REL;
+ }
+ update_tracker (av);
+ bytes_needed = size + av->consumption_since_last_update__;
+ if (bytes_needed <= 0)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Tracker %p delay for %u bytes is zero\n", av,
+ (unsigned int) size);
+ return GNUNET_TIME_UNIT_ZERO;
+ }
+ ret.rel_value =
+ (1000LL * bytes_needed) /
+ (unsigned long long) av->available_bytes_per_s__;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Tracker %p delay for %u bytes is %llu ms\n",
+ av, (unsigned int) size, (unsigned long long) ret.rel_value);
+ return ret;
+}
+
+
+/**
+ * Compute how many bytes are available for consumption right now.
+ * quota.
+ *
+ * @param av tracker to query
+ * @return number of bytes available for consumption right now
+ */
+int64_t
+GNUNET_BANDWIDTH_tracker_get_available (struct GNUNET_BANDWIDTH_Tracker * av)
+{
+ struct GNUNET_BANDWIDTH_Value32NBO bps;
+ uint64_t avail;
+ int64_t used;
+
+ update_tracker (av);
+ bps = GNUNET_BANDWIDTH_value_init (av->available_bytes_per_s__);
+ avail =
+ GNUNET_BANDWIDTH_value_get_available_until (bps,
+ GNUNET_TIME_absolute_get_duration
+ (av->last_update__));
+ used = av->consumption_since_last_update__;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tracker %p available bandwidth is %lld bytes\n", av,
+ (long long) (int64_t) (avail - used));
+ return (int64_t) (avail - used);
+}
+
+
+/**
+ * Update quota of bandwidth tracker.
+ *
+ * @param av tracker to initialize
+ * @param bytes_per_second_limit new limit to assume
+ */
+void
+GNUNET_BANDWIDTH_tracker_update_quota (struct GNUNET_BANDWIDTH_Tracker *av,
+ struct GNUNET_BANDWIDTH_Value32NBO
+ bytes_per_second_limit)
+{
+ uint32_t old_limit;
+ uint32_t new_limit;
+
+ new_limit = ntohl (bytes_per_second_limit.value__);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Tracker %p bandwidth changed to %u Bps\n", av,
+ (unsigned int) new_limit);
+ update_tracker (av);
+ old_limit = av->available_bytes_per_s__;
+ av->available_bytes_per_s__ = new_limit;
+ if (old_limit > new_limit)
+ update_tracker (av); /* maximum excess might be less now */
+}
+
+
+/* end of bandwidth.c */
diff --git a/src/util/bio.c b/src/util/bio.c
new file mode 100644
index 0000000..053b8e1
--- /dev/null
+++ b/src/util/bio.c
@@ -0,0 +1,525 @@
+/*
+ This file is part of GNUnet.
+ (C) 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/bio.c
+ * @brief functions for buffering IO
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_bio_lib.h"
+#include "gnunet_disk_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
+
+#define BIO_BUFFER_SIZE 65536
+
+#define MAX_META_DATA (1024 * 1024)
+
+/**
+ * Handle for buffered reading.
+ */
+struct GNUNET_BIO_ReadHandle
+{
+ struct GNUNET_DISK_FileHandle *fd;
+ char *emsg;
+ char *buffer;
+ size_t have;
+ size_t size;
+ off_t pos;
+};
+
+
+/**
+ * Open a file for reading.
+ *
+ * @param fn file name to be opened
+ * @return IO handle on success, NULL on error
+ */
+struct GNUNET_BIO_ReadHandle *
+GNUNET_BIO_read_open (const char *fn)
+{
+ struct GNUNET_DISK_FileHandle *fd;
+ struct GNUNET_BIO_ReadHandle *h;
+
+ fd = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
+ if (NULL == fd)
+ return NULL;
+ h = GNUNET_malloc (sizeof (struct GNUNET_BIO_ReadHandle) + BIO_BUFFER_SIZE);
+ h->buffer = (char *) &h[1];
+ h->size = BIO_BUFFER_SIZE;
+ h->fd = fd;
+ return h;
+}
+
+
+/**
+ * Close an open file. Reports if any errors reading
+ * from the file were encountered.
+ *
+ * @param h file handle
+ * @param emsg set to the error message
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_BIO_read_close (struct GNUNET_BIO_ReadHandle *h, char **emsg)
+{
+ int err;
+
+ err = (NULL == h->emsg) ? GNUNET_OK : GNUNET_SYSERR;
+ if (emsg != NULL)
+ *emsg = h->emsg;
+ else
+ GNUNET_free_non_null (h->emsg);
+ GNUNET_DISK_file_close (h->fd);
+ GNUNET_free (h);
+ return err;
+}
+
+
+/**
+ * Read the contents of a binary file into a buffer.
+ *
+ * @param h handle to an open file
+ * @param what describes what is being read (for error message creation)
+ * @param result the buffer to write the result to
+ * @param len the number of bytes to read
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_BIO_read (struct GNUNET_BIO_ReadHandle *h, const char *what,
+ void *result, size_t len)
+{
+ char *dst = result;
+ size_t min;
+ size_t pos;
+ ssize_t ret;
+
+ if (h->emsg != NULL)
+ return GNUNET_SYSERR;
+ pos = 0;
+ do
+ {
+ /* first, use buffer */
+ min = h->have - h->pos;
+ if (min > 0)
+ {
+ if (min > len - pos)
+ min = len - pos;
+ memcpy (&dst[pos], &h->buffer[h->pos], min);
+ h->pos += min;
+ pos += min;
+ }
+ if (pos == len)
+ return GNUNET_OK; /* done! */
+ GNUNET_assert (h->have == h->pos);
+ /* fill buffer */
+ ret = GNUNET_DISK_file_read (h->fd, h->buffer, h->size);
+ if (ret == -1)
+ {
+ GNUNET_asprintf (&h->emsg, _("Error reading `%s': %s"), what,
+ STRERROR (errno));
+ return GNUNET_SYSERR;
+ }
+ if (ret == 0)
+ {
+ GNUNET_asprintf (&h->emsg, _("Error reading `%s': %s"), what,
+ _("End of file"));
+ return GNUNET_SYSERR;
+ }
+ h->pos = 0;
+ h->have = ret;
+ }
+ while (pos < len); /* should always be true */
+ return GNUNET_OK;
+}
+
+
+/**
+ * Read the contents of a binary file into a buffer.
+ *
+ * @param h handle to an open file
+ * @param file name of the source file
+ * @param line line number in the source file
+ * @param result the buffer to write the result to
+ * @param len the number of bytes to read
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_BIO_read_fn (struct GNUNET_BIO_ReadHandle *h, const char *file, int line,
+ void *result, size_t len)
+{
+ char what[1024];
+
+ GNUNET_snprintf (what, sizeof (what), "%s:%d", file, line);
+ return GNUNET_BIO_read (h, what, result, len);
+}
+
+
+/**
+ * Read 0-terminated string from a file.
+ *
+ * @param h handle to an open file
+ * @param what describes what is being read (for error message creation)
+ * @param result the buffer to store a pointer to the (allocated) string to
+ * (note that *result could be set to NULL as well)
+ * @param maxLen maximum allowed length for the string
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_BIO_read_string (struct GNUNET_BIO_ReadHandle *h, const char *what,
+ char **result, size_t maxLen)
+{
+ char *buf;
+ uint32_t big;
+
+ if (GNUNET_OK != GNUNET_BIO_read_int32 (h, &big))
+ {
+ GNUNET_free_non_null (h->emsg);
+ GNUNET_asprintf (&h->emsg, _("Error reading length of string `%s'"), what);
+ return GNUNET_SYSERR;
+ }
+ if (big == 0)
+ {
+ *result = NULL;
+ return GNUNET_OK;
+ }
+ if (big > maxLen)
+ {
+ GNUNET_asprintf (&h->emsg, _("String `%s' longer than allowed (%u > %u)"),
+ what, big, maxLen);
+ return GNUNET_SYSERR;
+ }
+ buf = GNUNET_malloc (big);
+ *result = buf;
+ buf[--big] = '\0';
+ if (big == 0)
+ return GNUNET_OK;
+ if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, big))
+ {
+ GNUNET_free (buf);
+ *result = NULL;
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Read metadata container from a file.
+ *
+ * @param h handle to an open file
+ * @param what describes what is being read (for error message creation)
+ * @param result the buffer to store a pointer to the (allocated) metadata
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_BIO_read_meta_data (struct GNUNET_BIO_ReadHandle *h, const char *what,
+ struct GNUNET_CONTAINER_MetaData **result)
+{
+ uint32_t size;
+ char *buf;
+ struct GNUNET_CONTAINER_MetaData *meta;
+
+ if (GNUNET_BIO_read_int32 (h, (int32_t *) & size) != GNUNET_OK)
+ return GNUNET_SYSERR;
+ if (size == 0)
+ {
+ *result = NULL;
+ return GNUNET_OK;
+ }
+ if (size > MAX_META_DATA)
+ {
+ GNUNET_asprintf (&h->emsg,
+ _("Serialized metadata `%s' larger than allowed (%u>%u)"),
+ what, size, MAX_META_DATA);
+ return GNUNET_SYSERR;
+ }
+ buf = GNUNET_malloc (size);
+ if (GNUNET_OK != GNUNET_BIO_read (h, what, buf, size))
+ {
+ GNUNET_free (buf);
+ return GNUNET_SYSERR;
+ }
+ meta = GNUNET_CONTAINER_meta_data_deserialize (buf, size);
+ if (meta == NULL)
+ {
+ GNUNET_free (buf);
+ GNUNET_asprintf (&h->emsg, _("Metadata `%s' failed to deserialize"), what);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (buf);
+ *result = meta;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Read an (u)int32_t.
+ *
+ * @param h hande to open file
+ * @param file name of the source file
+ * @param line line number in the source file
+ * @param i address of 32-bit integer to read
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_read_int32__ (struct GNUNET_BIO_ReadHandle *h, const char *file,
+ int line, int32_t * i)
+{
+ int32_t big;
+
+ if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof (int32_t)))
+ return GNUNET_SYSERR;
+ *i = ntohl (big);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Read an (u)int64_t.
+ *
+ * @param h hande to open file
+ * @param file name of the source file
+ * @param line line number in the source file
+ * @param i address of 64-bit integer to read
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_read_int64__ (struct GNUNET_BIO_ReadHandle *h, const char *file,
+ int line, int64_t * i)
+{
+ int64_t big;
+
+ if (GNUNET_OK != GNUNET_BIO_read_fn (h, file, line, &big, sizeof (int64_t)))
+ return GNUNET_SYSERR;
+ *i = GNUNET_ntohll (big);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Handle for buffered writing.
+ */
+struct GNUNET_BIO_WriteHandle
+{
+ struct GNUNET_DISK_FileHandle *fd;
+ char *buffer;
+ size_t have;
+ size_t size;
+};
+
+
+/**
+ * Open a file for writing.
+ *
+ * @param fn file name to be opened
+ * @return IO handle on success, NULL on error
+ */
+struct GNUNET_BIO_WriteHandle *
+GNUNET_BIO_write_open (const char *fn)
+{
+ struct GNUNET_DISK_FileHandle *fd;
+ struct GNUNET_BIO_WriteHandle *h;
+
+ fd = GNUNET_DISK_file_open (fn,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
+ | GNUNET_DISK_OPEN_CREATE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ if (NULL == fd)
+ return NULL;
+ h = GNUNET_malloc (sizeof (struct GNUNET_BIO_WriteHandle) + BIO_BUFFER_SIZE);
+ h->buffer = (char *) &h[1];
+ h->size = BIO_BUFFER_SIZE;
+ h->fd = fd;
+
+ return h;
+}
+
+
+/**
+ * Close an open file for writing.
+ *
+ * @param h file handle
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_BIO_write_close (struct GNUNET_BIO_WriteHandle *h)
+{
+ ssize_t wrt;
+ int ret;
+
+ if (NULL == h->fd)
+ {
+ ret = GNUNET_SYSERR;
+ }
+ else
+ {
+ wrt = GNUNET_DISK_file_write (h->fd, h->buffer, h->have);
+ if (wrt == h->have)
+ ret = GNUNET_OK;
+ else
+ ret = GNUNET_SYSERR;
+ GNUNET_DISK_file_close (h->fd);
+ }
+ GNUNET_free (h);
+ return ret;
+}
+
+
+/**
+ * Write a buffer to a file.
+ *
+ * @param h handle to open file
+ * @param buffer the data to write
+ * @param n number of bytes to write
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_write (struct GNUNET_BIO_WriteHandle *h, const void *buffer,
+ size_t n)
+{
+ const char *src = buffer;
+ size_t min;
+ size_t pos;
+ ssize_t ret;
+
+ if (NULL == h->fd)
+ return GNUNET_SYSERR;
+ pos = 0;
+ do
+ {
+ /* first, just use buffer */
+ min = h->size - h->have;
+ if (min > n - pos)
+ min = n - pos;
+ memcpy (&h->buffer[h->have], &src[pos], min);
+ pos += min;
+ h->have += min;
+ if (pos == n)
+ return GNUNET_OK; /* done */
+ GNUNET_assert (h->have == h->size);
+ ret = GNUNET_DISK_file_write (h->fd, h->buffer, h->size);
+ if (ret != h->size)
+ {
+ GNUNET_DISK_file_close (h->fd);
+ h->fd = NULL;
+ return GNUNET_SYSERR; /* error */
+ }
+ h->have = 0;
+ }
+ while (pos < n); /* should always be true */
+ GNUNET_break (0);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Write a string to a file.
+ *
+ * @param h handle to open file
+ * @param s string to write (can be NULL)
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_write_string (struct GNUNET_BIO_WriteHandle *h, const char *s)
+{
+ uint32_t slen;
+
+ slen = (uint32_t) ((s == NULL) ? 0 : strlen (s) + 1);
+ if (GNUNET_OK != GNUNET_BIO_write_int32 (h, slen))
+ return GNUNET_SYSERR;
+ if (0 != slen)
+ return GNUNET_BIO_write (h, s, slen - 1);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Write metadata container to a file.
+ *
+ * @param h handle to open file
+ * @param m metadata to write
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_write_meta_data (struct GNUNET_BIO_WriteHandle *h,
+ const struct GNUNET_CONTAINER_MetaData *m)
+{
+ ssize_t size;
+ char *buf;
+
+ if (m == NULL)
+ return GNUNET_BIO_write_int32 (h, 0);
+ buf = NULL;
+ size =
+ GNUNET_CONTAINER_meta_data_serialize (m, &buf, MAX_META_DATA,
+ GNUNET_CONTAINER_META_DATA_SERIALIZE_PART);
+ if (size == -1)
+ {
+ GNUNET_free (buf);
+ return GNUNET_SYSERR;
+ }
+ if ((GNUNET_OK != GNUNET_BIO_write_int32 (h, (uint32_t) size)) ||
+ (GNUNET_OK != GNUNET_BIO_write (h, buf, size)))
+ {
+ GNUNET_free (buf);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (buf);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Write an (u)int32_t.
+ *
+ * @param h hande to open file
+ * @param i 32-bit integer to write
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_write_int32 (struct GNUNET_BIO_WriteHandle *h, int32_t i)
+{
+ int32_t big;
+
+ big = htonl (i);
+ return GNUNET_BIO_write (h, &big, sizeof (int32_t));
+}
+
+
+/**
+ * Write an (u)int64_t.
+ *
+ * @param h hande to open file
+ * @param i 64-bit integer to write
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_BIO_write_int64 (struct GNUNET_BIO_WriteHandle *h, int64_t i)
+{
+ int64_t big;
+
+ big = GNUNET_htonll (i);
+ return GNUNET_BIO_write (h, &big, sizeof (int64_t));
+}
+
+
+/* end of bio.c */
diff --git a/src/util/client.c b/src/util/client.c
new file mode 100644
index 0000000..2f09a90
--- /dev/null
+++ b/src/util/client.c
@@ -0,0 +1,1216 @@
+/*
+ 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_EXTRA_LOGGING
+
+/**
+ * How often do we re-try tranmsitting requests before giving up?
+ * Note that if we succeeded transmitting a request but failed to read
+ * a response, we do NOT re-try.
+ */
+#define MAX_ATTEMPTS 50
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
+
+/**
+ * Handle for a transmission request.
+ */
+struct GNUNET_CLIENT_TransmitHandle
+{
+ /**
+ * Connection state.
+ */
+ struct GNUNET_CLIENT_Connection *sock;
+
+ /**
+ * Function to call to get the data for transmission.
+ */
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ /**
+ * Closure for notify.
+ */
+ void *notify_cls;
+
+ /**
+ * Handle to the transmission with the underlying
+ * connection.
+ */
+ struct GNUNET_CONNECTION_TransmitHandle *th;
+
+ /**
+ * If we are re-trying and are delaying to do so,
+ * handle to the scheduled task managing the delay.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
+
+ /**
+ * Timeout for the operation overall.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Number of bytes requested.
+ */
+ size_t size;
+
+ /**
+ * Are we allowed to re-try to connect without telling
+ * the user (of this API) about the connection troubles?
+ */
+ int auto_retry;
+
+ /**
+ * Number of attempts left for transmitting the request. We may
+ * fail the first time (say because the service is not yet up), in
+ * which case (if auto_retry is set) we wait a bit and re-try
+ * (timeout permitting).
+ */
+ unsigned int attempts_left;
+
+};
+
+
+/**
+ * Context for processing
+ * "GNUNET_CLIENT_transmit_and_get_response" requests.
+ */
+struct TransmitGetResponseContext
+{
+ /**
+ * Client handle.
+ */
+ struct GNUNET_CLIENT_Connection *sock;
+
+ /**
+ * Message to transmit; do not free, allocated
+ * right after this struct.
+ */
+ const struct GNUNET_MessageHeader *hdr;
+
+ /**
+ * Timeout to use.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Function to call when done.
+ */
+ GNUNET_CLIENT_MessageHandler rn;
+
+ /**
+ * Closure for "rn".
+ */
+ void *rn_cls;
+};
+
+/**
+ * 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_CONNECTION_Handle *sock;
+
+ /**
+ * Our configuration.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Name of the service we interact with.
+ */
+ char *service_name;
+
+ /**
+ * Context of a transmit_and_get_response operation, NULL
+ * if no such operation is pending.
+ */
+ struct TransmitGetResponseContext *tag;
+
+ /**
+ * Handler for current receiver task.
+ */
+ GNUNET_CLIENT_MessageHandler receiver_handler;
+
+ /**
+ * Closure for receiver_handler.
+ */
+ void *receiver_handler_cls;
+
+ /**
+ * Handle for a pending transmission request, NULL if there is
+ * none pending.
+ */
+ struct GNUNET_CLIENT_TransmitHandle *th;
+
+ /**
+ * Handler for service test completion (NULL unless in service_test)
+ */
+ GNUNET_SCHEDULER_Task test_cb;
+
+ /**
+ * Deadline for calling 'test_cb'.
+ */
+ struct GNUNET_TIME_Absolute test_deadline;
+
+ /**
+ * If we are re-trying and are delaying to do so,
+ * handle to the scheduled task managing the delay.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier receive_task;
+
+ /**
+ * 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;
+
+ /**
+ * Current value for our incremental back-off (for
+ * connect re-tries).
+ */
+ struct GNUNET_TIME_Relative back_off;
+
+ /**
+ * Number of bytes in received_buf that are valid.
+ */
+ size_t received_pos;
+
+ /**
+ * Size of received_buf.
+ */
+ unsigned int received_size;
+
+ /**
+ * Do we have a complete response in received_buf?
+ */
+ int msg_complete;
+
+ /**
+ * Are we currently busy doing receive-processing?
+ * GNUNET_YES if so, GNUNET_NO if not.
+ */
+ int in_receive;
+
+ /**
+ * How often have we tried to connect?
+ */
+ unsigned int attempts;
+
+};
+
+
+/**
+ * Try to connect to the service.
+ *
+ * @param service_name name of service to connect to
+ * @param cfg configuration to use
+ * @param attempt counter used to alternate between IP and UNIX domain sockets
+ * @return NULL on error
+ */
+static struct GNUNET_CONNECTION_Handle *
+do_connect (const char *service_name,
+ const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned int attempt)
+{
+ struct GNUNET_CONNECTION_Handle *sock;
+ char *hostname;
+ char *unixpath;
+ unsigned long long port;
+
+ sock = NULL;
+#if AF_UNIX
+ if (0 == (attempt % 2))
+ {
+ /* on even rounds, try UNIX */
+ unixpath = NULL;
+ if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "UNIXPATH", &unixpath)) && (0 < strlen (unixpath))) /* We have a non-NULL unixpath, does that mean it's valid? */
+ {
+ sock = GNUNET_CONNECTION_create_from_connect_to_unixpath (cfg, unixpath);
+ if (sock != NULL)
+ {
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Connected to unixpath `%s'!\n",
+ unixpath);
+#endif
+ GNUNET_free (unixpath);
+ return sock;
+ }
+ }
+ GNUNET_free_non_null (unixpath);
+ }
+#endif
+
+ if (GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (cfg, service_name, "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)))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _
+ ("Could not determine valid hostname and port for service `%s' from configuration.\n"),
+ service_name);
+ return NULL;
+ }
+ if (0 == strlen (hostname))
+ {
+ GNUNET_free (hostname);
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Need a non-empty hostname for service `%s'.\n"), service_name);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* unspecified means 0 (disabled) */
+ port = 0;
+ hostname = NULL;
+ }
+ if (port == 0)
+ {
+#if AF_UNIX
+ if (0 != (attempt % 2))
+ {
+ /* try UNIX */
+ unixpath = NULL;
+ if ((GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, service_name, "UNIXPATH",
+ &unixpath)) &&
+ (0 < strlen (unixpath)))
+ {
+ sock =
+ GNUNET_CONNECTION_create_from_connect_to_unixpath (cfg, unixpath);
+ if (sock != NULL)
+ {
+ GNUNET_free (unixpath);
+ GNUNET_free_non_null (hostname);
+ return sock;
+ }
+ }
+ GNUNET_free_non_null (unixpath);
+ }
+#endif
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Port is 0 for service `%s', UNIXPATH did not work, returning NULL!\n",
+ service_name);
+#endif
+ GNUNET_free_non_null (hostname);
+ return NULL;
+ }
+
+ sock = GNUNET_CONNECTION_create_from_connect (cfg, hostname, port);
+ GNUNET_free (hostname);
+ return sock;
+}
+
+
+/**
+ * Get a connection with a service.
+ *
+ * @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 (const char *service_name,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct GNUNET_CLIENT_Connection *ret;
+ struct GNUNET_CONNECTION_Handle *sock;
+
+ sock = do_connect (service_name, cfg, 0);
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_Connection));
+ ret->attempts = 1;
+ ret->sock = sock;
+ ret->service_name = GNUNET_strdup (service_name);
+ ret->cfg = cfg;
+ ret->back_off = GNUNET_TIME_UNIT_MILLISECONDS;
+ return ret;
+}
+
+
+/**
+ * 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). Any pending
+ * transmission request will also be cancelled UNLESS the callback for
+ * the transmission request has already been called, in which case the
+ * transmission 'finish_pending_write' argument determines whether or
+ * not the write is guaranteed to complete before the socket is fully
+ * destroyed (unless, of course, there is an error with the server in
+ * which case the message may still be lost).
+ *
+ * @param finish_pending_write should a transmission already passed to the
+ * handle be completed?
+ * @param sock handle to the service connection
+ */
+void
+GNUNET_CLIENT_disconnect (struct GNUNET_CLIENT_Connection *sock,
+ int finish_pending_write)
+{
+ if (sock->in_receive == GNUNET_YES)
+ {
+ GNUNET_CONNECTION_receive_cancel (sock->sock);
+ sock->in_receive = GNUNET_NO;
+ }
+ if (sock->th != NULL)
+ {
+ GNUNET_CLIENT_notify_transmit_ready_cancel (sock->th);
+ sock->th = NULL;
+ }
+ if (NULL != sock->sock)
+ {
+ GNUNET_CONNECTION_destroy (sock->sock, finish_pending_write);
+ sock->sock = NULL;
+ }
+ if (sock->receive_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sock->receive_task);
+ sock->receive_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (sock->tag != NULL)
+ {
+ GNUNET_free (sock->tag);
+ sock->tag = NULL;
+ }
+ sock->receiver_handler = NULL;
+ GNUNET_array_grow (sock->received_buf, sock->received_size, 0);
+ GNUNET_free (sock->service_name);
+ GNUNET_free (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 "errCode" 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_CLIENT_MessageHandler receive_handler;
+ void *receive_handler_cls;
+
+ GNUNET_assert (conn->msg_complete == GNUNET_NO);
+ conn->in_receive = GNUNET_NO;
+ if ((available == 0) || (conn->sock == NULL) || (errCode != 0))
+ {
+ /* signal timeout! */
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Timeout in receive_helper, available %u, conn->sock %s, errCode `%s'\n",
+ (unsigned int) available, conn->sock == NULL ? "NULL" : "non-NULL",
+ STRERROR (errCode));
+#endif
+ if (NULL != (receive_handler = conn->receiver_handler))
+ {
+ receive_handler_cls = conn->receiver_handler_cls;
+ conn->receiver_handler = NULL;
+ receive_handler (receive_handler_cls, 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.rel_value == 0)
+ {
+ /* signal timeout! */
+ if (NULL != conn->receiver_handler)
+ 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.
+ *
+ * @param cls our handle to the client connection
+ * @param tc scheduler context
+ */
+static void
+receive_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CLIENT_Connection *sock = cls;
+ GNUNET_CLIENT_MessageHandler handler = sock->receiver_handler;
+ const struct GNUNET_MessageHeader *cmsg =
+ (const struct GNUNET_MessageHeader *) sock->received_buf;
+ void *handler_cls = sock->receiver_handler_cls;
+ uint16_t msize = ntohs (cmsg->size);
+ char mbuf[msize];
+ struct GNUNET_MessageHeader *msg = (struct GNUNET_MessageHeader *) mbuf;
+
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Received message of type %u and size %u\n",
+ ntohs (cmsg->type), msize);
+#endif
+ sock->receive_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_assert (GNUNET_YES == sock->msg_complete);
+ GNUNET_assert (sock->received_pos >= 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);
+ if (handler != NULL)
+ handler (handler_cls, msg);
+}
+
+
+/**
+ * Read from the service.
+ *
+ * @param sock the service
+ * @param handler function to call with the message
+ * @param handler_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 *handler_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! */
+ if (NULL != handler)
+ handler (handler_cls, NULL);
+ return;
+ }
+ sock->receiver_handler = handler;
+ sock->receiver_handler_cls = handler_cls;
+ sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ if (GNUNET_YES == sock->msg_complete)
+ {
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->receive_task);
+ sock->receive_task = GNUNET_SCHEDULER_add_now (&receive_task, sock);
+ }
+ else
+ {
+ GNUNET_assert (sock->in_receive == GNUNET_NO);
+ sock->in_receive = GNUNET_YES;
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "calling GNUNET_CONNECTION_receive\n");
+#endif
+ GNUNET_CONNECTION_receive (sock->sock, GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ timeout, &receive_helper, sock);
+ }
+}
+
+
+/**
+ * Report service unavailable.
+ */
+static void
+service_test_error (GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ GNUNET_SCHEDULER_add_continuation (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
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Received confirmation that service is running.\n");
+#endif
+ GNUNET_SCHEDULER_add_continuation (conn->test_cb, conn->test_cb_cls,
+ GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+ }
+ else
+ {
+ service_test_error (conn->test_cb, conn->test_cb_cls);
+ }
+ GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
+}
+
+
+/**
+ * Send the 'TEST' message to the service. If successful, prepare to
+ * receive the reply.
+ *
+ * @param cls the 'struct GNUNET_CLIENT_Connection' of the connection to test
+ * @param size number of bytes available in buf
+ * @param buf where to write the message
+ * @return number of bytes written to buf
+ */
+static size_t
+write_test (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_CLIENT_Connection *conn = cls;
+ struct GNUNET_MessageHeader *msg;
+
+ if (size < sizeof (struct GNUNET_MessageHeader))
+ {
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, _("Failure to transmit TEST request.\n"));
+#endif
+ service_test_error (conn->test_cb, conn->test_cb_cls);
+ GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
+ return 0; /* client disconnected */
+ }
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmitting `%s' request.\n", "TEST");
+#endif
+ msg = (struct GNUNET_MessageHeader *) buf;
+ msg->type = htons (GNUNET_MESSAGE_TYPE_TEST);
+ msg->size = htons (sizeof (struct GNUNET_MessageHeader));
+ GNUNET_CLIENT_receive (conn, &confirm_handler, conn,
+ GNUNET_TIME_absolute_get_remaining
+ (conn->test_deadline));
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+/**
+ * Test if the service is running. If we are given a UNIXPATH or a local address,
+ * we do this NOT by trying to connect to the service, but by trying to BIND to
+ * the same port. If the BIND fails, we know the service is running.
+ *
+ * @param service name of the service to wait for
+ * @param cfg configuration to use
+ * @param timeout how long to wait at most
+ * @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 (const char *service,
+ const struct GNUNET_CONFIGURATION_Handle *cfg,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ char *hostname;
+ unsigned long long port;
+ struct GNUNET_NETWORK_Handle *sock;
+ struct GNUNET_CLIENT_Connection *conn;
+
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Testing if service `%s' is running.\n",
+ service);
+#endif
+#ifdef AF_UNIX
+ {
+ /* probe UNIX support */
+ struct sockaddr_un s_un;
+ size_t slen;
+ char *unixpath;
+
+ unixpath = NULL;
+ if ((GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, service, "UNIXPATH", &unixpath)) && (0 < strlen (unixpath))) /* We have a non-NULL unixpath, does that mean it's valid? */
+ {
+ if (strlen (unixpath) >= sizeof (s_un.sun_path))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath,
+ sizeof (s_un.sun_path));
+ }
+ else
+ {
+ sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0);
+ if (sock != NULL)
+ {
+ memset (&s_un, 0, sizeof (s_un));
+ s_un.sun_family = AF_UNIX;
+ slen = strlen (unixpath) + 1;
+ if (slen >= sizeof (s_un.sun_path))
+ slen = sizeof (s_un.sun_path) - 1;
+ memcpy (s_un.sun_path, unixpath, slen);
+ s_un.sun_path[slen] = '\0';
+ slen = sizeof (struct sockaddr_un);
+#if LINUX
+ s_un.sun_path[0] = '\0';
+#endif
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ s_un.sun_len = (u_char) slen;
+#endif
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_un,
+ slen))
+ {
+ /* failed to bind => service must be running */
+ GNUNET_free (unixpath);
+ (void) GNUNET_NETWORK_socket_close (sock);
+ GNUNET_SCHEDULER_add_continuation (task, task_cls,
+ GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+ return;
+ }
+ (void) GNUNET_NETWORK_socket_close (sock);
+ }
+ /* let's try IP */
+ }
+ }
+ GNUNET_free_non_null (unixpath);
+ }
+#endif
+
+ hostname = NULL;
+ if ((GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_number (cfg, service, "PORT", &port)) ||
+ (port > 65535) ||
+ (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg, service, "HOSTNAME",
+ &hostname)))
+ {
+ /* UNIXPATH failed (if possible) AND IP failed => error */
+ service_test_error (task, task_cls);
+ return;
+ }
+
+ if (0 == strcmp ("localhost", hostname)
+#if !LINUX
+ && 0
+#endif
+ )
+ {
+ /* can test using 'bind' */
+ struct sockaddr_in s_in;
+
+ memset (&s_in, 0, sizeof (s_in));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ s_in.sin_len = sizeof (struct sockaddr_in);
+#endif
+ s_in.sin_family = AF_INET;
+ s_in.sin_port = htons (port);
+
+ sock = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
+ if (sock != NULL)
+ {
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in,
+ sizeof (s_in)))
+ {
+ /* failed to bind => service must be running */
+ GNUNET_free (hostname);
+ (void) GNUNET_NETWORK_socket_close (sock);
+ GNUNET_SCHEDULER_add_continuation (task, task_cls,
+ GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+ return;
+ }
+ (void) GNUNET_NETWORK_socket_close (sock);
+ }
+ }
+
+ if (0 == strcmp ("ip6-localhost", hostname)
+#if !LINUX
+ && 0
+#endif
+ )
+ {
+ /* can test using 'bind' */
+ struct sockaddr_in6 s_in6;
+
+ memset (&s_in6, 0, sizeof (s_in6));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ s_in6.sin6_len = sizeof (struct sockaddr_in6);
+#endif
+ s_in6.sin6_family = AF_INET6;
+ s_in6.sin6_port = htons (port);
+
+ sock = GNUNET_NETWORK_socket_create (AF_INET6, SOCK_STREAM, 0);
+ if (sock != NULL)
+ {
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) &s_in6,
+ sizeof (s_in6)))
+ {
+ /* failed to bind => service must be running */
+ GNUNET_free (hostname);
+ (void) GNUNET_NETWORK_socket_close (sock);
+ GNUNET_SCHEDULER_add_continuation (task, task_cls,
+ GNUNET_SCHEDULER_REASON_PREREQ_DONE);
+ return;
+ }
+ (void) GNUNET_NETWORK_socket_close (sock);
+ }
+ }
+
+ if (((0 == strcmp ("localhost", hostname)) ||
+ (0 == strcmp ("ip6-localhost", hostname)))
+#if !LINUX
+ && 0
+#endif
+ )
+ {
+ /* all binds succeeded => claim service not running right now */
+ GNUNET_free_non_null (hostname);
+ service_test_error (task, task_cls);
+ return;
+ }
+ GNUNET_free_non_null (hostname);
+
+ /* non-localhost, try 'connect' method */
+ conn = GNUNET_CLIENT_connect (service, cfg);
+ if (conn == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Could not connect to service `%s', must not be running.\n"),
+ service);
+ service_test_error (task, task_cls);
+ return;
+ }
+ conn->test_cb = task;
+ conn->test_cb_cls = task_cls;
+ conn->test_deadline = GNUNET_TIME_relative_to_absolute (timeout);
+
+ if (NULL ==
+ GNUNET_CLIENT_notify_transmit_ready (conn,
+ sizeof (struct GNUNET_MessageHeader),
+ timeout, GNUNET_YES, &write_test,
+ conn))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Failure to transmit request to service `%s'\n"), service);
+ service_test_error (task, task_cls);
+ GNUNET_CLIENT_disconnect (conn, GNUNET_NO);
+ return;
+ }
+}
+
+
+/**
+ * Connection notifies us about failure or success of
+ * a transmission request. Either pass it on to our
+ * user or, if possible, retry.
+ *
+ * @param cls our "struct GNUNET_CLIENT_TransmissionHandle"
+ * @param size number of bytes available for transmission
+ * @param buf where to write them
+ * @return number of bytes written to buf
+ */
+static size_t
+client_notify (void *cls, size_t size, void *buf);
+
+
+/**
+ * This task is run if we should re-try connection to the
+ * service after a while.
+ *
+ * @param cls our "struct GNUNET_CLIENT_TransmitHandle" of the request
+ * @param tc unused
+ */
+static void
+client_delayed_retry (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CLIENT_TransmitHandle *th = cls;
+ struct GNUNET_TIME_Relative delay;
+
+ th->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Transmission failed due to shutdown.\n");
+#endif
+ th->sock->th = NULL;
+ th->notify (th->notify_cls, 0, NULL);
+ GNUNET_free (th);
+ return;
+ }
+ th->sock->sock =
+ do_connect (th->sock->service_name, th->sock->cfg, th->sock->attempts++);
+ if (NULL == th->sock->sock)
+ {
+ /* could happen if we're out of sockets */
+ delay =
+ GNUNET_TIME_relative_min (GNUNET_TIME_absolute_get_remaining
+ (th->timeout), th->sock->back_off);
+ th->sock->back_off =
+ GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply
+ (th->sock->back_off, 2),
+ GNUNET_TIME_UNIT_SECONDS);
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission failed %u times, trying again in %llums.\n",
+ MAX_ATTEMPTS - th->attempts_left,
+ (unsigned long long) delay.rel_value);
+#endif
+ th->reconnect_task =
+ GNUNET_SCHEDULER_add_delayed (delay, &client_delayed_retry, th);
+ return;
+ }
+ th->th =
+ GNUNET_CONNECTION_notify_transmit_ready (th->sock->sock, th->size,
+ GNUNET_TIME_absolute_get_remaining
+ (th->timeout), &client_notify,
+ th);
+ if (th->th == NULL)
+ {
+ GNUNET_break (0);
+ th->sock->th = NULL;
+ th->notify (th->notify_cls, 0, NULL);
+ GNUNET_free (th);
+ return;
+ }
+}
+
+
+/**
+ * Connection notifies us about failure or success of a transmission
+ * request. Either pass it on to our user or, if possible, retry.
+ *
+ * @param cls our "struct GNUNET_CLIENT_TransmissionHandle"
+ * @param size number of bytes available for transmission
+ * @param buf where to write them
+ * @return number of bytes written to buf
+ */
+static size_t
+client_notify (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_CLIENT_TransmitHandle *th = cls;
+ size_t ret;
+ struct GNUNET_TIME_Relative delay;
+
+ th->th = NULL;
+ th->sock->th = NULL;
+ if (buf == NULL)
+ {
+ delay = GNUNET_TIME_absolute_get_remaining (th->timeout);
+ delay.rel_value /= 2;
+ if ((0 !=
+ (GNUNET_SCHEDULER_REASON_SHUTDOWN & GNUNET_SCHEDULER_get_reason ())) ||
+ (GNUNET_YES != th->auto_retry) || (0 == --th->attempts_left) ||
+ (delay.rel_value < 1))
+ {
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission failed %u times, giving up.\n",
+ MAX_ATTEMPTS - th->attempts_left);
+#endif
+ GNUNET_break (0 == th->notify (th->notify_cls, 0, NULL));
+ GNUNET_free (th);
+ return 0;
+ }
+ /* auto-retry */
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to connect to `%s', automatically trying again.\n",
+ th->sock->service_name);
+#endif
+ GNUNET_CONNECTION_destroy (th->sock->sock, GNUNET_NO);
+ th->sock->sock = NULL;
+ delay = GNUNET_TIME_relative_min (delay, th->sock->back_off);
+ th->sock->back_off =
+ GNUNET_TIME_relative_min (GNUNET_TIME_relative_multiply
+ (th->sock->back_off, 2),
+ GNUNET_TIME_UNIT_SECONDS);
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission failed %u times, trying again in %llums.\n",
+ MAX_ATTEMPTS - th->attempts_left,
+ (unsigned long long) delay.rel_value);
+#endif
+ th->sock->th = th;
+ th->reconnect_task =
+ GNUNET_SCHEDULER_add_delayed (delay, &client_delayed_retry, th);
+ return 0;
+ }
+ GNUNET_assert (size >= th->size);
+ ret = th->notify (th->notify_cls, size, buf);
+ GNUNET_free (th);
+ return ret;
+}
+
+
+/**
+ * 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 sock 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 auto_retry if the connection to the service dies, should we
+ * automatically re-connect and retry (within the timeout period)
+ * or should we immediately fail in this case? Pass GNUNET_YES
+ * if the caller does not care about temporary connection errors,
+ * for example because the protocol is stateless
+ * @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_CLIENT_TransmitHandle *
+GNUNET_CLIENT_notify_transmit_ready (struct GNUNET_CLIENT_Connection *sock,
+ size_t size,
+ struct GNUNET_TIME_Relative timeout,
+ int auto_retry,
+ GNUNET_CONNECTION_TransmitReadyNotify
+ notify, void *notify_cls)
+{
+ struct GNUNET_CLIENT_TransmitHandle *th;
+
+ if (NULL != sock->th)
+ {
+ /* If this breaks, you most likley called this function twice without waiting
+ * for completion or canceling the request */
+ GNUNET_break (0);
+ return NULL;
+ }
+ th = GNUNET_malloc (sizeof (struct GNUNET_CLIENT_TransmitHandle));
+ th->sock = sock;
+ th->size = size;
+ th->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ th->auto_retry = auto_retry;
+ th->notify = notify;
+ th->notify_cls = notify_cls;
+ th->attempts_left = MAX_ATTEMPTS;
+ sock->th = th;
+ if (sock->sock == NULL)
+ {
+ th->reconnect_task =
+ GNUNET_SCHEDULER_add_delayed (sock->back_off, &client_delayed_retry,
+ th);
+
+ }
+ else
+ {
+ th->th =
+ GNUNET_CONNECTION_notify_transmit_ready (sock->sock, size, timeout,
+ &client_notify, th);
+ if (NULL == th->th)
+ {
+ GNUNET_break (0);
+ GNUNET_free (th);
+ sock->th = NULL;
+ return NULL;
+ }
+ }
+ return th;
+}
+
+
+/**
+ * Cancel a request for notification.
+ *
+ * @param th handle from the original request.
+ */
+void
+GNUNET_CLIENT_notify_transmit_ready_cancel (struct GNUNET_CLIENT_TransmitHandle
+ *th)
+{
+ if (th->reconnect_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_assert (NULL == th->th);
+ GNUNET_SCHEDULER_cancel (th->reconnect_task);
+ th->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ else
+ {
+ GNUNET_assert (NULL != th->th);
+ GNUNET_CONNECTION_notify_transmit_ready_cancel (th->th);
+ }
+ th->sock->th = NULL;
+ GNUNET_free (th);
+}
+
+
+/**
+ * Function called to notify a client about the socket
+ * begin ready to queue the message. "buf" will be
+ * NULL and "size" zero if the socket was closed for
+ * writing in the meantime.
+ *
+ * @param cls closure of type "struct TransmitGetResponseContext*"
+ * @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_for_response (void *cls, size_t size, void *buf)
+{
+ struct TransmitGetResponseContext *tc = cls;
+ uint16_t msize;
+
+ tc->sock->tag = NULL;
+ msize = ntohs (tc->hdr->size);
+ if (NULL == buf)
+ {
+#if DEBUG_CLIENT
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ _("Could not submit request, not expecting to receive a response.\n"));
+#endif
+ if (NULL != tc->rn)
+ tc->rn (tc->rn_cls, NULL);
+ GNUNET_free (tc);
+ return 0;
+ }
+ GNUNET_assert (size >= msize);
+ memcpy (buf, tc->hdr, msize);
+ GNUNET_CLIENT_receive (tc->sock, tc->rn, tc->rn_cls,
+ GNUNET_TIME_absolute_get_remaining (tc->timeout));
+ GNUNET_free (tc);
+ return msize;
+}
+
+
+/**
+ * Convenience API that combines sending a request
+ * to the service and waiting for a response.
+ * If either operation times out, the callback
+ * will be called with a "NULL" response (in which
+ * case the connection should probably be destroyed).
+ *
+ * @param sock connection to use
+ * @param hdr message to transmit
+ * @param timeout when to give up (for both transmission
+ * and for waiting for a response)
+ * @param auto_retry if the connection to the service dies, should we
+ * automatically re-connect and retry (within the timeout period)
+ * or should we immediately fail in this case? Pass GNUNET_YES
+ * if the caller does not care about temporary connection errors,
+ * for example because the protocol is stateless
+ * @param rn function to call with the response
+ * @param rn_cls closure for rn
+ * @return GNUNET_OK on success, GNUNET_SYSERR if a request
+ * is already pending
+ */
+int
+GNUNET_CLIENT_transmit_and_get_response (struct GNUNET_CLIENT_Connection *sock,
+ const struct GNUNET_MessageHeader *hdr,
+ struct GNUNET_TIME_Relative timeout,
+ int auto_retry,
+ GNUNET_CLIENT_MessageHandler rn,
+ void *rn_cls)
+{
+ struct TransmitGetResponseContext *tc;
+ uint16_t msize;
+
+ if (NULL != sock->th)
+ return GNUNET_SYSERR;
+ GNUNET_assert (sock->tag == NULL);
+ msize = ntohs (hdr->size);
+ tc = GNUNET_malloc (sizeof (struct TransmitGetResponseContext) + msize);
+ tc->sock = sock;
+ tc->hdr = (const struct GNUNET_MessageHeader *) &tc[1];
+ memcpy (&tc[1], hdr, msize);
+ tc->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ tc->rn = rn;
+ tc->rn_cls = rn_cls;
+ if (NULL ==
+ GNUNET_CLIENT_notify_transmit_ready (sock, msize, timeout, auto_retry,
+ &transmit_for_response, tc))
+ {
+ GNUNET_break (0);
+ GNUNET_free (tc);
+ return GNUNET_SYSERR;
+ }
+ sock->tag = tc;
+ return GNUNET_OK;
+}
+
+
+
+/* end of client.c */
diff --git a/src/util/common_allocation.c b/src/util/common_allocation.c
new file mode 100644
index 0000000..5e1f75e
--- /dev/null
+++ b/src/util/common_allocation.c
@@ -0,0 +1,359 @@
+/*
+ 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/common_allocation.c
+ * @brief wrapper around malloc/free
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#ifndef INT_MAX
+#define INT_MAX 0x7FFFFFFF
+#endif
+
+#if 0
+#define W32_MEM_LIMIT 200000000
+#endif
+
+#ifdef W32_MEM_LIMIT
+static LONG mem_used = 0;
+#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_malloc
+ * @param linenumber where in the code was the call to GNUNET_malloc
+ * @return pointer to size bytes of memory
+ */
+void *
+GNUNET_xmalloc_ (size_t size, const char *filename, int linenumber)
+{
+ void *ret;
+
+ /* As a security precaution, we generally do not allow very large
+ * allocations using the default 'GNUNET_malloc' macro */
+ GNUNET_assert_at (size <= GNUNET_MAX_MALLOC_CHECKED, filename, linenumber);
+ ret = GNUNET_xmalloc_unchecked_ (size, filename, linenumber);
+ if (ret == NULL)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "malloc");
+ GNUNET_abort ();
+ }
+ return ret;
+}
+
+
+/**
+ * Allocate and initialize memory. Checks the return value, aborts if no more
+ * memory is available. Don't use GNUNET_xmemdup_ directly. Use the
+ * GNUNET_memdup macro.
+ *
+ * @param buf buffer to initialize from (must contain size bytes)
+ * @param size number of bytes to allocate
+ * @param filename where is this call being made (for debugging)
+ * @param linenumber line where this call is being made (for debugging)
+ * @return allocated memory, never NULL
+ */
+void *
+GNUNET_xmemdup_ (const void *buf, size_t size, const char *filename,
+ int linenumber)
+{
+ void *ret;
+
+ /* As a security precaution, we generally do not allow very large
+ * allocations here */
+ GNUNET_assert_at (size <= GNUNET_MAX_MALLOC_CHECKED, filename, linenumber);
+#ifdef W32_MEM_LIMIT
+ size += sizeof (size_t);
+ if (mem_used + size > W32_MEM_LIMIT)
+ return NULL;
+#endif
+ GNUNET_assert_at (size < INT_MAX, filename, linenumber);
+ ret = malloc (size);
+ if (ret == NULL)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "malloc");
+ GNUNET_abort ();
+ }
+#ifdef W32_MEM_LIMIT
+ *((size_t *) ret) = size;
+ ret = &((size_t *) ret)[1];
+ mem_used += size;
+#endif
+ memcpy (ret, buf, size);
+ return ret;
+}
+
+
+
+/**
+ * Wrapper around malloc. Allocates size bytes of memory.
+ * The memory will be zero'ed out.
+ *
+ * @param size the number of bytes to allocate
+ * @param filename where in the code was the call to GNUNET_malloc_large
+ * @param linenumber where in the code was the call to GNUNET_malloc_large
+ * @return pointer to size bytes of memory, NULL if we do not have enough memory
+ */
+void *
+GNUNET_xmalloc_unchecked_ (size_t size, const char *filename, int linenumber)
+{
+ void *result;
+
+#ifdef W32_MEM_LIMIT
+ size += sizeof (size_t);
+ if (mem_used + size > W32_MEM_LIMIT)
+ return NULL;
+#endif
+
+ result = malloc (size);
+ if (result == NULL)
+ return NULL;
+ memset (result, 0, size);
+
+#ifdef W32_MEM_LIMIT
+ *((size_t *) result) = size;
+ result = &((size_t *) result)[1];
+ mem_used += size;
+#endif
+
+ return result;
+}
+
+
+/**
+ * Reallocate memory. Checks the return value, aborts if no more
+ * memory is available.
+ *
+ * @param ptr the pointer to reallocate
+ * @param n how many bytes of memory to allocate
+ * @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, size_t n, const char *filename, int linenumber)
+{
+#ifdef W32_MEM_LIMIT
+ n += sizeof (size_t);
+ ptr = &((size_t *) ptr)[-1];
+ mem_used = mem_used - *((size_t *) ptr) + n;
+#endif
+ ptr = realloc (ptr, n);
+ if ((NULL == ptr) && (n > 0))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "realloc");
+ GNUNET_abort ();
+ }
+#ifdef W32_MEM_LIMIT
+ ptr = &((size_t *) ptr)[1];
+#endif
+ 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);
+#ifdef W32_MEM_LIMIT
+ ptr = &((size_t *) ptr)[-1];
+ mem_used -= *((size_t *) ptr);
+#endif
+ 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_strdup
+ * @param linenumber where in the code was the call to GNUNET_strdup
+ * @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;
+}
+
+
+/**
+ * Dup partially a string (same semantics as strndup).
+ *
+ * @param str the string to dup
+ * @param len the length of the string to dup
+ * @param filename where in the code was the call to GNUNET_strndup
+ * @param linenumber where in the code was the call to GNUNET_strndup
+ * @return strndup(str,len)
+ */
+char *
+GNUNET_xstrndup_ (const char *str, size_t len, const char *filename,
+ int linenumber)
+{
+ char *res;
+
+ GNUNET_assert_at (str != NULL, filename, linenumber);
+ len = GNUNET_MIN (len, strlen (str));
+ res = GNUNET_xmalloc_ (len + 1, filename, linenumber);
+ memcpy (res, str, len);
+ res[len] = '\0';
+ 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;
+}
+
+
+/**
+ * Like asprintf, just portable.
+ *
+ * @param buf set to a buffer of sufficient size (allocated, caller must free)
+ * @param format format string (see printf, fprintf, etc.)
+ * @param ... data for format string
+ * @return number of bytes in "*buf" excluding 0-termination
+ */
+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;
+}
+
+
+/**
+ * Like snprintf, just aborts if the buffer is of insufficient size.
+ *
+ * @param buf pointer to buffer that is written to
+ * @param size number of bytes in buf
+ * @param format format strings
+ * @param ... data for format string
+ * @return number of bytes written to buf or negative value on error
+ */
+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;
+}
+
+
+/**
+ * Create a copy of the given message.
+ *
+ * @param msg message to copy
+ * @return duplicate of the message
+ */
+struct GNUNET_MessageHeader *
+GNUNET_copy_message (const struct GNUNET_MessageHeader *msg)
+{
+ struct GNUNET_MessageHeader *ret;
+ uint16_t msize;
+
+ msize = ntohs (msg->size);
+ GNUNET_assert (msize >= sizeof (struct GNUNET_MessageHeader));
+ ret = GNUNET_malloc (msize);
+ memcpy (ret, msg, msize);
+ 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 0000000..117e575
--- /dev/null
+++ b/src/util/common_endian.c
@@ -0,0 +1,94 @@
+/*
+ This file is part of GNUnet.
+ (C) 2001, 2002, 2003, 2004, 2006, 2012 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"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util",__VA_ARGS__)
+
+uint64_t
+GNUNET_ntohll (uint64_t n)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+ return n;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ return (((uint64_t) ntohl (n)) << 32) + ntohl (n >> 32);
+#else
+ #error byteorder undefined
+#endif
+}
+
+uint64_t
+GNUNET_htonll (uint64_t n)
+{
+#if __BYTE_ORDER == __BIG_ENDIAN
+ return n;
+#elif __BYTE_ORDER == __LITTLE_ENDIAN
+ return (((uint64_t) htonl (n)) << 32) + htonl (n >> 32);
+#else
+ #error byteorder undefined
+#endif
+}
+
+
+/**
+ * Convert double to network-byte-order.
+ * @param d the value in network byte order
+ * @return the same value in host byte order
+ */
+double
+GNUNET_hton_double (double d)
+{
+ double res;
+ uint64_t *in = (uint64_t *) &d;
+ uint64_t *out = (uint64_t *) &res;
+
+ out[0] = GNUNET_htonll(in[0]);
+
+ return res;
+}
+
+
+/**
+ * Convert double to host-byte-order
+ * @param d the value in network byte order
+ * @return the same value in host byte order
+ */
+double
+GNUNET_ntoh_double (double d)
+{
+ double res;
+ uint64_t *in = (uint64_t *) &d;
+ uint64_t *out = (uint64_t *) &res;
+
+ out[0] = GNUNET_ntohll(in[0]);
+
+ return res;
+}
+
+
+
+/* end of common_endian.c */
diff --git a/src/util/common_logging.c b/src/util/common_logging.c
new file mode 100644
index 0000000..e19aa8c
--- /dev/null
+++ b/src/util/common_logging.c
@@ -0,0 +1,1056 @@
+/*
+ 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"
+
+#include <regex.h>
+
+/**
+ * After how many milliseconds 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 do we use for matching of
+ * bulk components?
+ */
+#define COMP_TRACK_SIZE 32
+
+/**
+ * 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. Will be 0-terminated.
+ */
+static char last_bulk_comp[COMP_TRACK_SIZE + 1];
+
+/**
+ * Running component.
+ */
+static char *component;
+
+/**
+ * Running component (without pid).
+ */
+static char *component_nopid;
+
+/**
+ * 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.
+ */
+unsigned int skip_log;
+
+/**
+ * File descriptor to use for "stderr", or NULL for none.
+ */
+static FILE *GNUNET_stderr;
+
+/**
+ * Represents a single logging definition
+ */
+struct LogDef
+{
+ /**
+ * Component name regex
+ */
+ regex_t component_regex;
+
+ /**
+ * File name regex
+ */
+ regex_t file_regex;
+
+ /**
+ * Function name regex
+ */
+ regex_t function_regex;
+
+ /**
+ * Lowest line at which this definition matches.
+ * Defaults to 0. Must be <= to_line.
+ */
+ int from_line;
+
+ /**
+ * Highest line at which this definition matches.
+ * Defaults to INT_MAX. Must be >= from_line.
+ */
+ int to_line;
+
+ /**
+ * Maximal log level allowed for calls that match this definition.
+ * Calls with higher log level will be disabled.
+ * Must be >= 0
+ */
+ int level;
+
+ /**
+ * 1 if this definition comes from GNUNET_FORCE_LOG, which means that it
+ * overrides any configuration options. 0 otherwise.
+ */
+ int force;
+};
+
+/**
+ * Dynamic array of logging definitions
+ */
+struct LogDef *logdefs = NULL;
+
+/**
+ * Allocated size of logdefs array (in units)
+ */
+int logdefs_size = 0;
+
+/**
+ * The number of units used in logdefs array.
+ */
+int logdefs_len = 0;
+
+/**
+ * GNUNET_YES if GNUNET_LOG environment variable is already parsed.
+ */
+int gnunet_log_parsed = GNUNET_NO;
+
+/**
+ * GNUNET_YES if GNUNET_FORCE_LOG environment variable is already parsed.
+ */
+int gnunet_force_log_parsed = GNUNET_NO;
+
+/**
+ * GNUNET_YES if at least one definition with forced == 1 is available.
+ */
+int gnunet_force_log_present = GNUNET_NO;
+
+#ifdef WINDOWS
+/**
+ * Contains the number of performance counts per second.
+ */
+LARGE_INTEGER performance_frequency;
+#endif
+
+/**
+ * Convert a textual description of a loglevel
+ * to the respective GNUNET_GE_KIND.
+ *
+ * @param log loglevel to parse
+ * @return GNUNET_GE_INVALID if log does not parse
+ */
+static enum GNUNET_ErrorType
+get_type (const char *log)
+{
+ if (log == NULL)
+ return GNUNET_ERROR_TYPE_UNSPECIFIED;
+ 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;
+ if (0 == strcasecmp (log, _("NONE")))
+ return GNUNET_ERROR_TYPE_NONE;
+ return GNUNET_ERROR_TYPE_INVALID;
+}
+
+#if !defined(GNUNET_CULL_LOGGING)
+/**
+ * Utility function - reallocates logdefs array to be twice as large.
+ */
+static void
+resize_logdefs ()
+{
+ logdefs_size = (logdefs_size + 1) * 2;
+ logdefs = GNUNET_realloc (logdefs, logdefs_size * sizeof (struct LogDef));
+}
+
+
+/**
+ * Abort the process, generate a core dump if possible.
+ */
+void
+GNUNET_abort ()
+{
+#if WINDOWS
+ DebugBreak ();
+#endif
+ abort ();
+}
+
+
+/**
+ * Utility function - adds a parsed definition to logdefs array.
+ *
+ * @param component see struct LogDef, can't be NULL
+ * @param file see struct LogDef, can't be NULL
+ * @param function see struct LogDef, can't be NULL
+ * @param from_line see struct LogDef
+ * @param to_line see struct LogDef
+ * @param level see struct LogDef, must be >= 0
+ * @param force see struct LogDef
+ * @return 0 on success, regex-specific error otherwise
+ */
+static int
+add_definition (char *component, char *file, char *function, int from_line,
+ int to_line, int level, int force)
+{
+ struct LogDef n;
+ int r;
+
+ if (logdefs_size == logdefs_len)
+ resize_logdefs ();
+ memset (&n, 0, sizeof (n));
+ if (strlen (component) == 0)
+ component = (char *) ".*";
+ r = regcomp (&n.component_regex, (const char *) component, REG_NOSUB);
+ if (r != 0)
+ {
+ return r;
+ }
+ if (strlen (file) == 0)
+ file = (char *) ".*";
+ r = regcomp (&n.file_regex, (const char *) file, REG_NOSUB);
+ if (r != 0)
+ {
+ regfree (&n.component_regex);
+ return r;
+ }
+ if ((NULL == function) || (strlen (function) == 0))
+ function = (char *) ".*";
+ r = regcomp (&n.function_regex, (const char *) function, REG_NOSUB);
+ if (r != 0)
+ {
+ regfree (&n.component_regex);
+ regfree (&n.file_regex);
+ return r;
+ }
+ n.from_line = from_line;
+ n.to_line = to_line;
+ n.level = level;
+ n.force = force;
+ logdefs[logdefs_len++] = n;
+ return 0;
+}
+
+
+/**
+ * Decides whether a particular logging call should or should not be allowed
+ * to be made. Used internally by GNUNET_log*()
+ *
+ * @param caller_level loglevel the caller wants to use
+ * @param comp component name the caller uses (NULL means that global
+ * component name is used)
+ * @param file file name containing the logging call, usually __FILE__
+ * @param function function which tries to make a logging call,
+ * usually __FUNCTION__
+ * @param line line at which the call is made, usually __LINE__
+ * @return 0 to disallow the call, 1 to allow it
+ */
+int
+GNUNET_get_log_call_status (int caller_level, const char *comp,
+ const char *file, const char *function, int line)
+{
+ struct LogDef *ld;
+ int i;
+ int force_only;
+
+ if (comp == NULL)
+ /* Use default component */
+ comp = component_nopid;
+
+ /* We have no definitions to override globally configured log level,
+ * so just use it right away.
+ */
+ if (min_level >= 0 && gnunet_force_log_present == GNUNET_NO)
+ return caller_level <= min_level;
+
+ /* Only look for forced definitions? */
+ force_only = min_level >= 0;
+ for (i = 0; i < logdefs_len; i++)
+ {
+ ld = &logdefs[i];
+ if ((!force_only || ld->force) &&
+ (line >= ld->from_line && line <= ld->to_line) &&
+ (regexec (&ld->component_regex, comp, 0, NULL, 0) == 0) &&
+ (regexec (&ld->file_regex, file, 0, NULL, 0) == 0) &&
+ (regexec (&ld->function_regex, function, 0, NULL, 0) == 0))
+ {
+ /* We're finished */
+ return caller_level <= ld->level;
+ }
+ }
+ /* No matches - use global level, if defined */
+ if (min_level >= 0)
+ return caller_level <= min_level;
+ /* All programs/services previously defaulted to WARNING.
+ * Now WE default to WARNING, and THEY default to NULL.
+ */
+ return caller_level <= GNUNET_ERROR_TYPE_WARNING;
+}
+
+
+/**
+ * Utility function - parses a definition
+ *
+ * Definition format:
+ * component;file;function;from_line-to_line;level[/component...]
+ * All entries are mandatory, but may be empty.
+ * Empty entries for component, file and function are treated as
+ * "matches anything".
+ * Empty line entry is treated as "from 0 to INT_MAX"
+ * Line entry with only one line is treated as "this line only"
+ * Entry for level MUST NOT be empty.
+ * Entries for component, file and function that consist of a
+ * single character "*" are treated (at the moment) the same way
+ * empty entries are treated (wildcard matching is not implemented (yet?)).
+ * file entry is matched to the end of __FILE__. That is, it might be
+ * a base name, or a base name with leading directory names (some compilers
+ * define __FILE__ to absolute file path).
+ *
+ * @param constname name of the environment variable from which to get the
+ * string to be parsed
+ * @param force 1 if definitions found in constname are to be forced
+ * @return number of added definitions
+ */
+static int
+parse_definitions (const char *constname, int force)
+{
+ char *def;
+ const char *tmp;
+ char *comp = NULL;
+ char *file = NULL;
+ char *function = NULL;
+ char *p;
+ char *start;
+ char *t;
+ short state;
+ int level;
+ int from_line, to_line;
+ int counter = 0;
+ int keep_looking = 1;
+
+ tmp = getenv (constname);
+ if (tmp == NULL)
+ return 0;
+ def = GNUNET_strdup (tmp);
+ from_line = 0;
+ to_line = INT_MAX;
+ for (p = def, state = 0, start = def; keep_looking; p++)
+ {
+ switch (p[0])
+ {
+ case ';': /* found a field separator */
+ p[0] = '\0';
+ switch (state)
+ {
+ case 0: /* within a component name */
+ comp = start;
+ break;
+ case 1: /* within a file name */
+ file = start;
+ break;
+ case 2: /* within a function name */
+ /* after a file name there must be a function name */
+ function = start;
+ break;
+ case 3: /* within a from-to line range */
+ if (strlen (start) > 0)
+ {
+ errno = 0;
+ from_line = strtol (start, &t, 10);
+ if (errno != 0 || from_line < 0)
+ {
+ GNUNET_free (def);
+ return counter;
+ }
+ if (t < p && t[0] == '-')
+ {
+ errno = 0;
+ start = t + 1;
+ to_line = strtol (start, &t, 10);
+ if (errno != 0 || to_line < 0 || t != p)
+ {
+ GNUNET_free (def);
+ return counter;
+ }
+ }
+ else /* one number means "match this line only" */
+ to_line = from_line;
+ }
+ else /* default to 0-max */
+ {
+ from_line = 0;
+ to_line = INT_MAX;
+ }
+ break;
+ }
+ start = p + 1;
+ state += 1;
+ break;
+ case '\0': /* found EOL */
+ keep_looking = 0;
+ /* fall through to '/' */
+ case '/': /* found a definition separator */
+ switch (state)
+ {
+ case 4: /* within a log level */
+ p[0] = '\0';
+ state = 0;
+ level = get_type ((const char *) start);
+ if (level == GNUNET_ERROR_TYPE_INVALID ||
+ level == GNUNET_ERROR_TYPE_UNSPECIFIED ||
+ 0 != add_definition (comp, file, function, from_line, to_line,
+ level, force))
+ {
+ GNUNET_free (def);
+ return counter;
+ }
+ counter += 1;
+ start = p + 1;
+ break;
+ default:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+ GNUNET_free (def);
+ return counter;
+}
+
+/**
+ * Utility function - parses GNUNET_LOG and GNUNET_FORCE_LOG.
+ */
+static void
+parse_all_definitions ()
+{
+ if (gnunet_log_parsed == GNUNET_NO)
+ parse_definitions ("GNUNET_LOG", 0);
+ gnunet_log_parsed = GNUNET_YES;
+ if (gnunet_force_log_parsed == GNUNET_NO)
+ gnunet_force_log_present =
+ parse_definitions ("GNUNET_FORCE_LOG", 1) > 0 ? GNUNET_YES : GNUNET_NO;
+ gnunet_force_log_parsed = GNUNET_YES;
+}
+#endif
+/**
+ * Setup logging.
+ *
+ * @param comp default component to use
+ * @param loglevel what types of messages should be logged
+ * @param logfile which file to write log messages to (can be NULL)
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_log_setup (const char *comp, const char *loglevel, const char *logfile)
+{
+ FILE *altlog;
+ int dirwarn;
+ char *fn;
+ const char *env_logfile = NULL;
+ int altlog_fd;
+
+ min_level = get_type (loglevel);
+#if !defined(GNUNET_CULL_LOGGING)
+ parse_all_definitions ();
+#endif
+#ifdef WINDOWS
+ QueryPerformanceFrequency (&performance_frequency);
+#endif
+ GNUNET_free_non_null (component);
+ GNUNET_asprintf (&component, "%s-%d", comp, getpid ());
+ GNUNET_free_non_null (component_nopid);
+ component_nopid = GNUNET_strdup (comp);
+
+ env_logfile = getenv ("GNUNET_FORCE_LOGFILE");
+ if ((env_logfile != NULL) && (strlen (env_logfile) > 0))
+ logfile = env_logfile;
+
+ if (logfile == NULL)
+ return GNUNET_OK;
+ fn = GNUNET_STRINGS_filename_expand (logfile);
+ if (NULL == fn)
+ return GNUNET_SYSERR;
+ dirwarn = (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn));
+ altlog_fd = OPEN (fn, O_APPEND |
+#if WINDOWS
+ O_BINARY |
+#endif
+ O_WRONLY | O_CREAT,
+#if WINDOWS
+ _S_IREAD | _S_IWRITE
+#else
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
+#endif
+ );
+ if (altlog_fd != -1)
+ {
+ int dup_return;
+ if (GNUNET_stderr != NULL)
+ fclose (GNUNET_stderr);
+ dup_return = dup2 (altlog_fd, 2);
+ close (altlog_fd);
+ if (dup_return != -1)
+ {
+ altlog = fdopen (2, "ab");
+ if (altlog == NULL)
+ {
+ close (2);
+ altlog_fd = -1;
+ }
+ }
+ else
+ {
+ altlog_fd = -1;
+ }
+ }
+ if (altlog_fd == -1)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, "open", fn);
+ if (dirwarn)
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to create or access directory for log file `%s'\n"),
+ fn);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ GNUNET_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);
+}
+
+
+/**
+ * Actually output the log message.
+ *
+ * @param kind how severe was the issue
+ * @param comp component responsible
+ * @param datestr current date/time
+ * @param msg the actual message
+ */
+static void
+output_message (enum GNUNET_ErrorType kind, const char *comp,
+ const char *datestr, const char *msg)
+{
+ struct CustomLogger *pos;
+
+ if (GNUNET_stderr != NULL)
+ {
+ FPRINTF (GNUNET_stderr, "%s %s %s %s", datestr, comp,
+ GNUNET_error_type_to_string (kind), msg);
+ fflush (GNUNET_stderr);
+ }
+ pos = loggers;
+ while (pos != NULL)
+ {
+ pos->logger (pos->logger_cls, kind, comp, datestr, msg);
+ pos = pos->next;
+ }
+}
+
+
+/**
+ * Flush an existing bulk report to the output.
+ *
+ * @param datestr our current timestamp
+ */
+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.abs_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
+ * @param check_reset GNUNET_YES to assert that the log skip counter is currently zero
+ */
+void
+GNUNET_log_skip (unsigned int n, int check_reset)
+{
+ if (n == 0)
+ {
+ int ok;
+
+ ok = (0 == skip_log);
+ skip_log = 0;
+ if (check_reset)
+ GNUNET_assert (ok);
+ }
+ else
+ skip_log += n;
+}
+
+
+/**
+ * Output a log message using the default mechanism.
+ *
+ * @param kind how severe was the issue
+ * @param comp component responsible
+ * @param message the actual message
+ * @param va arguments to the format string "message"
+ */
+static void
+mylog (enum GNUNET_ErrorType kind, const char *comp, const char *message,
+ va_list va)
+{
+ char date[DATE_STR_SIZE];
+ char date2[DATE_STR_SIZE];
+ time_t timetmp;
+ struct timeval timeofday;
+ struct tm *tmptr;
+ size_t size;
+ va_list vacp;
+
+ va_copy (vacp, va);
+ size = VSNPRINTF (NULL, 0, message, vacp) + 1;
+ GNUNET_assert (0 != size);
+ va_end (vacp);
+ {
+ char buf[size];
+
+ VSNPRINTF (buf, size, message, va);
+ time (&timetmp);
+ memset (date, 0, DATE_STR_SIZE);
+ tmptr = localtime (&timetmp);
+ gettimeofday (&timeofday, NULL);
+ if (NULL != tmptr)
+ {
+#ifdef WINDOWS
+ LARGE_INTEGER pc;
+
+ pc.QuadPart = 0;
+ QueryPerformanceCounter (&pc);
+ strftime (date2, DATE_STR_SIZE, "%b %d %H:%M:%S-%%020llu", tmptr);
+ snprintf (date, sizeof (date), date2,
+ (long long) (pc.QuadPart /
+ (performance_frequency.QuadPart / 1000)));
+#else
+ strftime (date2, DATE_STR_SIZE, "%b %d %H:%M:%S-%%06u", tmptr);
+ snprintf (date, sizeof (date), date2, timeofday.tv_usec);
+#endif
+ }
+ else
+ strcpy (date, "localtime error");
+ if ((0 != (kind & GNUNET_ERROR_TYPE_BULK)) &&
+ (last_bulk_time.abs_value != 0) &&
+ (0 == strncmp (buf, last_bulk, sizeof (last_bulk))))
+ {
+ last_bulk_repeat++;
+ if ((GNUNET_TIME_absolute_get_duration (last_bulk_time).rel_value >
+ BULK_DELAY_THRESHOLD) || (last_bulk_repeat > BULK_REPEAT_THRESHOLD))
+ flush_bulk (date);
+ 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 ();
+ strncpy (last_bulk_comp, comp, COMP_TRACK_SIZE);
+ output_message (kind, comp, date, buf);
+ }
+}
+
+
+/**
+ * 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_nocheck (enum GNUNET_ErrorType kind, const char *message, ...)
+{
+ va_list va;
+
+ va_start (va, message);
+ mylog (kind, component, message, va);
+ va_end (va);
+}
+
+
+/**
+ * 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_nocheck (enum GNUNET_ErrorType kind, const char *comp,
+ const char *message, ...)
+{
+ va_list va;
+ char comp_w_pid[128];
+
+ if (comp == NULL)
+ comp = component_nopid;
+
+ va_start (va, message);
+ GNUNET_snprintf (comp_w_pid, sizeof (comp_w_pid), "%s-%d", comp, getpid ());
+ mylog (kind, comp_w_pid, message, va);
+ va_end (va);
+}
+
+
+/**
+ * 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)
+{
+ 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");
+ if ((kind & ~GNUNET_ERROR_TYPE_BULK) == 0)
+ return _("NONE");
+ return _("INVALID");
+}
+
+
+/**
+ * Convert a hash to a string (for printing debug messages).
+ * This is one of the very few calls in the entire API that is
+ * NOT reentrant!
+ *
+ * @param hc the hash code
+ * @return string form; will be overwritten by next call to GNUNET_h2s.
+ */
+const char *
+GNUNET_h2s (const GNUNET_HashCode * hc)
+{
+ static struct GNUNET_CRYPTO_HashAsciiEncoded ret;
+
+ GNUNET_CRYPTO_hash_to_enc (hc, &ret);
+ ret.encoding[8] = '\0';
+ return (const char *) ret.encoding;
+}
+
+/**
+ * Convert a hash to a string (for printing debug messages).
+ * This is one of the very few calls in the entire API that is
+ * NOT reentrant!
+ *
+ * @param hc the hash code
+ * @return string form; will be overwritten by next call to GNUNET_h2s_full.
+ */
+const char *
+GNUNET_h2s_full (const GNUNET_HashCode * hc)
+{
+ static struct GNUNET_CRYPTO_HashAsciiEncoded ret;
+
+ GNUNET_CRYPTO_hash_to_enc (hc, &ret);
+ ret.encoding[sizeof (ret) - 1] = '\0';
+ return (const char *) ret.encoding;
+}
+
+/**
+ * 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;
+}
+
+/**
+ * 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_full (const struct GNUNET_PeerIdentity *pid)
+{
+ static struct GNUNET_CRYPTO_HashAsciiEncoded ret;
+
+ GNUNET_CRYPTO_hash_to_enc (&pid->hashPubKey, &ret);
+ return (const char *) ret.encoding;
+}
+
+
+/**
+ * Convert a "struct sockaddr*" (IPv4 or IPv6 address) to a string
+ * (for printing debug messages). This is one of the very few calls
+ * in the entire API that is NOT reentrant!
+ *
+ * @param addr the address
+ * @param addrlen the length of the address
+ * @return nicely formatted string for the address
+ * will be overwritten by next call to GNUNET_a2s.
+ */
+const char *
+GNUNET_a2s (const struct sockaddr *addr, socklen_t addrlen)
+{
+ static char buf[INET6_ADDRSTRLEN + 8];
+ static char b2[6];
+ const struct sockaddr_in *v4;
+ const struct sockaddr_un *un;
+ const struct sockaddr_in6 *v6;
+ unsigned int off;
+
+ if (addr == NULL)
+ return _("unknown address");
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ if (addrlen != sizeof (struct sockaddr_in))
+ return "<invalid v4 address>";
+ v4 = (const struct sockaddr_in *) addr;
+ inet_ntop (AF_INET, &v4->sin_addr, buf, INET_ADDRSTRLEN);
+ if (0 == ntohs (v4->sin_port))
+ return buf;
+ strcat (buf, ":");
+ GNUNET_snprintf (b2, sizeof (b2), "%u", ntohs (v4->sin_port));
+ strcat (buf, b2);
+ return buf;
+ case AF_INET6:
+ if (addrlen != sizeof (struct sockaddr_in6))
+ return "<invalid v4 address>";
+ v6 = (const struct sockaddr_in6 *) addr;
+ buf[0] = '[';
+ inet_ntop (AF_INET6, &v6->sin6_addr, &buf[1], INET6_ADDRSTRLEN);
+ if (0 == ntohs (v6->sin6_port))
+ return &buf[1];
+ strcat (buf, "]:");
+ GNUNET_snprintf (b2, sizeof (b2), "%u", ntohs (v6->sin6_port));
+ strcat (buf, b2);
+ return buf;
+ case AF_UNIX:
+ if (addrlen <= sizeof (sa_family_t))
+ return "<unbound UNIX client>";
+ un = (const struct sockaddr_un *) addr;
+ off = 0;
+ if (un->sun_path[0] == '\0')
+ off++;
+ snprintf (buf, sizeof (buf), "%s%.*s", (off == 1) ? "@" : "",
+ (int) (addrlen - sizeof (sa_family_t) - 1 - off),
+ &un->sun_path[off]);
+ return buf;
+ default:
+ return _("invalid address");
+ }
+}
+
+
+/**
+ * Initializer
+ */
+void __attribute__ ((constructor)) GNUNET_util_cl_init ()
+{
+ GNUNET_stderr = stderr;
+#ifdef MINGW
+ GNInitWinEnv (NULL);
+#endif
+}
+
+
+/**
+ * Destructor
+ */
+void __attribute__ ((destructor)) GNUNET_util_cl_fini ()
+{
+#ifdef MINGW
+ GNShutdownWinEnv ();
+#endif
+}
+
+/* end of common_logging.c */
diff --git a/src/util/configuration.c b/src/util/configuration.c
new file mode 100644
index 0000000..f24b2c2
--- /dev/null
+++ b/src/util/configuration.c
@@ -0,0 +1,1287 @@
+/*
+ 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_util_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_strings_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * @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;
+
+};
+
+
+/**
+ * Used for diffing a configuration object against
+ * the default one
+ */
+struct DiffHandle
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfgDefault;
+ struct GNUNET_CONFIGURATION_Handle *cfgDiff;
+};
+
+
+
+/**
+ * Create a GNUNET_CONFIGURATION_Handle.
+ *
+ * @return fresh configuration object
+ */
+struct GNUNET_CONFIGURATION_Handle *
+GNUNET_CONFIGURATION_create ()
+{
+ return GNUNET_malloc (sizeof (struct GNUNET_CONFIGURATION_Handle));
+}
+
+
+/**
+ * Destroy configuration object.
+ *
+ * @param cfg configuration to destroy
+ */
+void
+GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct ConfigSection *sec;
+
+ while (NULL != (sec = cfg->sections))
+ GNUNET_CONFIGURATION_remove_section (cfg, sec->name);
+ GNUNET_free (cfg);
+}
+
+
+/**
+ * Parse a configuration file, add all of the options in the
+ * file to the configuration environment.
+ *
+ * @param cfg configuration to update
+ * @param filename name of the configuration file
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+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);
+ if (fn == NULL)
+ return GNUNET_SYSERR;
+ dirty = cfg->dirty; /* back up value! */
+ if (NULL == (fp = FOPEN (fn, "r")))
+ {
+ 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 ((unsigned char) line[i]));
+ i--)
+ line[i] = '\0';
+ if (1 == sscanf (line, "@INLINE@ %191[^\n]", value))
+ {
+ /* @INLINE@ value */
+ if (GNUNET_OK != 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 ((unsigned char) 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 */
+ 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;
+}
+
+
+/**
+ * Test if there are configuration options that were
+ * changed since the last save.
+ *
+ * @param cfg configuration to inspect
+ * @return GNUNET_NO if clean, GNUNET_YES if dirty, GNUNET_SYSERR on error (i.e. last save failed)
+ */
+int
+GNUNET_CONFIGURATION_is_dirty (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ return cfg->dirty;
+}
+
+
+/**
+ * Write configuration file.
+ *
+ * @param cfg configuration to write
+ * @param filename where to write the configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg,
+ 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);
+ if (fn == NULL)
+ return GNUNET_SYSERR;
+ if (GNUNET_OK != GNUNET_DISK_directory_create_for_file (fn))
+ {
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ if (NULL == (fp = FOPEN (fn, "w")))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fopen", fn);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ error = 0;
+ sec = cfg->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, "%s\n", ""))
+ {
+ error = 1;
+ break;
+ }
+ sec = sec->next;
+ }
+ if (error != 0)
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fprintf", filename);
+ GNUNET_assert (0 == FCLOSE (fp));
+ if (error != 0)
+ {
+ cfg->dirty = GNUNET_SYSERR; /* last write failed */
+ return GNUNET_SYSERR;
+ }
+ cfg->dirty = GNUNET_NO; /* last write succeeded */
+ return GNUNET_OK;
+}
+
+
+/**
+ * Iterate over all options in the configuration.
+ *
+ * @param cfg configuration to inspect
+ * @param iter function to call on each option
+ * @param iter_cls closure for iter
+ */
+void
+GNUNET_CONFIGURATION_iterate (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ GNUNET_CONFIGURATION_Iterator iter,
+ void *iter_cls)
+{
+ struct ConfigSection *spos;
+ struct ConfigEntry *epos;
+
+ spos = cfg->sections;
+ while (spos != NULL)
+ {
+ epos = spos->entries;
+ while (epos != NULL)
+ {
+ iter (iter_cls, spos->name, epos->key, epos->val);
+ epos = epos->next;
+ }
+ spos = spos->next;
+ }
+}
+
+
+/**
+ * Iterate over values of a section in the configuration.
+ *
+ * @param cfg configuration to inspect
+ * @param section the section
+ * @param iter function to call on each option
+ * @param iter_cls closure for iter
+ */
+void
+GNUNET_CONFIGURATION_iterate_section_values (const struct
+ GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ GNUNET_CONFIGURATION_Iterator iter,
+ void *iter_cls)
+{
+ struct ConfigSection *spos;
+ struct ConfigEntry *epos;
+
+ spos = cfg->sections;
+ while ((spos != NULL) && (0 != strcasecmp (spos->name, section)))
+ spos = spos->next;
+
+ if (spos == NULL)
+ return;
+
+ epos = spos->entries;
+ while (epos != NULL)
+ {
+ iter (iter_cls, spos->name, epos->key, epos->val);
+ epos = epos->next;
+ }
+}
+
+
+/**
+ * Iterate over all sections in the configuration.
+ *
+ * @param cfg configuration to inspect
+ * @param iter function to call on each section
+ * @param iter_cls closure for iter
+ */
+void
+GNUNET_CONFIGURATION_iterate_sections (const struct GNUNET_CONFIGURATION_Handle
+ *cfg,
+ GNUNET_CONFIGURATION_Section_Iterator
+ iter, void *iter_cls)
+{
+ struct ConfigSection *spos;
+ struct ConfigSection *next;
+
+ next = cfg->sections;
+ while (next != NULL)
+ {
+ spos = next;
+ next = spos->next;
+ iter (iter_cls, spos->name);
+ }
+}
+
+/**
+ * Remove the given section and all options in it.
+ *
+ * @param cfg configuration to inspect
+ * @param section name of the section to remove
+ */
+void
+GNUNET_CONFIGURATION_remove_section (struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section)
+{
+ struct ConfigSection *spos;
+ struct ConfigSection *prev;
+ struct ConfigEntry *ent;
+
+ prev = NULL;
+ spos = cfg->sections;
+ while (spos != NULL)
+ {
+ if (0 == strcasecmp (section, spos->name))
+ {
+ if (prev == NULL)
+ cfg->sections = spos->next;
+ else
+ prev->next = spos->next;
+ while (NULL != (ent = spos->entries))
+ {
+ spos->entries = ent->next;
+ GNUNET_free (ent->key);
+ GNUNET_free_non_null (ent->val);
+ GNUNET_free (ent);
+ cfg->dirty = GNUNET_YES;
+ }
+ GNUNET_free (spos->name);
+ GNUNET_free (spos);
+ return;
+ }
+ prev = spos;
+ spos = spos->next;
+ }
+}
+
+
+/**
+ * Copy a configuration value to the given target configuration.
+ * Overwrites existing entries.
+ *
+ * @param cls the destination configuration (struct GNUNET_CONFIGURATION_Handle*)
+ * @param section section for the value
+ * @param option option name of the value
+ * @param value value to copy
+ */
+static void
+copy_entry (void *cls, const char *section, const char *option,
+ const char *value)
+{
+ struct GNUNET_CONFIGURATION_Handle *dst = cls;
+
+ GNUNET_CONFIGURATION_set_value_string (dst, section, option, value);
+}
+
+
+/**
+ * Duplicate an existing configuration object.
+ *
+ * @param cfg configuration to duplicate
+ * @return duplicate configuration
+ */
+struct GNUNET_CONFIGURATION_Handle *
+GNUNET_CONFIGURATION_dup (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ struct GNUNET_CONFIGURATION_Handle *ret;
+
+ ret = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_iterate (cfg, &copy_entry, ret);
+ return ret;
+}
+
+
+/**
+ * FIXME.
+ *
+ * @param cfg FIXME
+ * @param section FIXME
+ * @return matching entry, NULL if not found
+ */
+static struct ConfigSection *
+findSection (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section)
+{
+ struct ConfigSection *pos;
+
+ pos = cfg->sections;
+ while ((pos != NULL) && (0 != strcasecmp (section, pos->name)))
+ pos = pos->next;
+ return pos;
+}
+
+
+/**
+ * Find an entry from a configuration.
+ *
+ * @param cfg handle to the configuration
+ * @param section section the option is in
+ * @param key the option
+ * @return matching entry, NULL if not found
+ */
+static struct ConfigEntry *
+findEntry (const struct GNUNET_CONFIGURATION_Handle *cfg, const char *section,
+ const char *key)
+{
+ struct ConfigSection *sec;
+ struct ConfigEntry *pos;
+
+ sec = findSection (cfg, section);
+ if (sec == NULL)
+ return NULL;
+ pos = sec->entries;
+ while ((pos != NULL) && (0 != strcasecmp (key, pos->key)))
+ pos = pos->next;
+ return pos;
+}
+
+
+/**
+ * A callback function, compares entries from two configurations
+ * (default against a new configuration) and write the diffs in a
+ * diff-configuration object (the callback object).
+ *
+ * @param cls the diff configuration (struct DiffHandle*)
+ * @param section section for the value (of the default conf.)
+ * @param option option name of the value (of the default conf.)
+ * @param value value to copy (of the default conf.)
+ */
+static void
+compareEntries (void *cls, const char *section, const char *option,
+ const char *value)
+{
+ struct DiffHandle *dh = cls;
+ struct ConfigEntry *entNew;
+
+ entNew = findEntry (dh->cfgDefault, section, option);
+ if ((entNew != NULL) && (strcmp (entNew->val, value) == 0))
+ return;
+ GNUNET_CONFIGURATION_set_value_string (dh->cfgDiff, section, option, value);
+}
+
+
+/**
+ * Write only configuration entries that have been changed to configuration file
+ * @param cfgDefault default configuration
+ * @param cfgNew new configuration
+ * @param filename where to write the configuration diff between default and new
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_CONFIGURATION_write_diffs (const struct GNUNET_CONFIGURATION_Handle
+ *cfgDefault,
+ const struct GNUNET_CONFIGURATION_Handle
+ *cfgNew, const char *filename)
+{
+ int ret;
+ struct DiffHandle diffHandle;
+
+ diffHandle.cfgDiff = GNUNET_CONFIGURATION_create ();
+ diffHandle.cfgDefault = cfgDefault;
+ GNUNET_CONFIGURATION_iterate (cfgNew, compareEntries, &diffHandle);
+ ret = GNUNET_CONFIGURATION_write (diffHandle.cfgDiff, filename);
+ GNUNET_CONFIGURATION_destroy (diffHandle.cfgDiff);
+ return ret;
+}
+
+
+/**
+ * Set a configuration value that should be a string.
+ *
+ * @param cfg configuration to update
+ * @param section section of interest
+ * @param option option of interest
+ * @param value value to set
+ */
+void
+GNUNET_CONFIGURATION_set_value_string (struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section, const char *option,
+ const char *value)
+{
+ struct ConfigSection *sec;
+ struct ConfigEntry *e;
+
+ e = findEntry (cfg, section, option);
+ if (e != NULL)
+ {
+ GNUNET_free_non_null (e->val);
+ e->val = GNUNET_strdup (value);
+ return;
+ }
+ sec = findSection (cfg, section);
+ if (sec == NULL)
+ {
+ sec = GNUNET_malloc (sizeof (struct ConfigSection));
+ sec->name = GNUNET_strdup (section);
+ sec->next = cfg->sections;
+ cfg->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;
+}
+
+
+/**
+ * Set a configuration value that should be a number.
+ *
+ * @param cfg configuration to update
+ * @param section section of interest
+ * @param option option of interest
+ * @param number value to set
+ */
+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);
+}
+
+
+/**
+ * Get a configuration value that should be a number.
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @param number where to store the numeric value of the option
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_CONFIGURATION_get_value_number (const 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;
+}
+
+
+/**
+ * Get a configuration value that should be a relative time.
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @param time set to the time value stored in the configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_CONFIGURATION_get_value_time (const struct GNUNET_CONFIGURATION_Handle
+ *cfg, const char *section,
+ const char *option,
+ struct GNUNET_TIME_Relative *time)
+{
+ struct ConfigEntry *e;
+
+ e = findEntry (cfg, section, option);
+ if (e == NULL)
+ return GNUNET_SYSERR;
+
+ return GNUNET_STRINGS_fancy_time_to_relative (e->val, time);
+}
+
+
+/**
+ * Get a configuration value that should be a size in bytes.
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @param size set to the size in bytes as stored in the configuration
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_CONFIGURATION_get_value_size (const struct GNUNET_CONFIGURATION_Handle
+ *cfg, const char *section,
+ const char *option,
+ unsigned long long *size)
+{
+ struct ConfigEntry *e;
+
+ e = findEntry (cfg, section, option);
+ if (e == NULL)
+ return GNUNET_SYSERR;
+ return GNUNET_STRINGS_fancy_size_to_bytes (e->val, size);
+}
+
+
+/**
+ * Get a configuration value that should be a string.
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @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 (const 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;
+}
+
+
+/**
+ * Get a configuration value that should be in a set of
+ * predefined strings
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @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 (const 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)
+ {
+ 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
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @return GNUNET_YES if so, GNUNET_NO if not.
+ */
+int
+GNUNET_CONFIGURATION_have_value (const 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 cfg configuration to use for path expansion
+ * @param orig string to $-expand (will be freed!)
+ * @return $-expanded string
+ */
+char *
+GNUNET_CONFIGURATION_expand_dollar (const 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_filename (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 cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @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 (const struct
+ GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ const char *option, char **value)
+{
+ char *tmp;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &tmp))
+ {
+ *value = NULL;
+ return GNUNET_SYSERR;
+ }
+ tmp = GNUNET_CONFIGURATION_expand_dollar (cfg, tmp);
+ *value = GNUNET_STRINGS_filename_expand (tmp);
+ GNUNET_free (tmp);
+ if (*value == NULL)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Get a configuration value that should be in a set of
+ * "GNUNET_YES" or "GNUNET_NO".
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @return GNUNET_YES, GNUNET_NO or GNUNET_SYSERR
+ */
+int
+GNUNET_CONFIGURATION_get_value_yesno (const 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.
+ *
+ * @param cfg configuration to inspect
+ * @param section section of interest
+ * @param option option of interest
+ * @param cb function to call on each filename
+ * @param cb_cls closure for cb
+ * @return number of filenames iterated over, -1 on error
+ */
+int
+GNUNET_CONFIGURATION_iterate_value_filenames (const struct
+ GNUNET_CONFIGURATION_Handle *cfg,
+ const char *section,
+ const char *option,
+ GNUNET_FileNameCallback cb,
+ void *cb_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 (cb_cls, pos)))
+ {
+ ret = GNUNET_SYSERR;
+ break;
+ }
+ }
+ if (old == '\0')
+ break;
+ pos = end + 1;
+ }
+ GNUNET_free (list);
+ return ret;
+}
+
+
+/**
+ * FIXME.
+ *
+ * @param value FIXME
+ * @return FIXME
+ */
+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;
+}
+
+
+/**
+ * FIXME.
+ *
+ * @param cls string we compare with (const char*)
+ * @param fn filename we are currently looking at
+ * @return GNUNET_OK if the names do not match, GNUNET_SYSERR if they do
+ */
+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 cfg configuration to update
+ * @param section section of interest
+ * @param option option of interest
+ * @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);
+ if (strlen (old) > 0)
+ 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 cfg configuration to update
+ * @param section section of interest
+ * @param option option of interest
+ * @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;
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg, section, option, &list))
+ return GNUNET_NO;
+ match = escape_name (value);
+ 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 (0 == strcmp (pos, match))
+ {
+ if (old != '\0')
+ memmove (pos, &end[1], strlen (&end[1]) + 1);
+ else
+ {
+ if (pos != list)
+ pos[-1] = '\0';
+ else
+ pos[0] = '\0';
+ }
+ GNUNET_CONFIGURATION_set_value_string (cfg, section, option, list);
+ GNUNET_free (list);
+ GNUNET_free (match);
+ return GNUNET_OK;
+ }
+ if (old == '\0')
+ break;
+ end[0] = old;
+ pos = end + 1;
+ }
+ GNUNET_free (list);
+ GNUNET_free (match);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Wrapper around GNUNET_CONFIGURATION_parse.
+ *
+ * @param cls the cfg
+ * @param filename file to parse
+ * @return GNUNET_OK on success
+ */
+static int
+parse_configuration_file (void *cls, const char *filename)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ int ret;
+
+ ret = GNUNET_CONFIGURATION_parse (cfg, filename);
+ return ret;
+}
+
+
+/**
+ * Load configuration (starts with defaults, then loads
+ * system-specific configuration).
+ *
+ * @param cfg configuration to update
+ * @param filename name of the configuration file, NULL to load defaults
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *filename)
+{
+ 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", ipath, "config.d");
+ GNUNET_free (ipath);
+ if (GNUNET_SYSERR ==
+ GNUNET_DISK_directory_scan (baseconfig, &parse_configuration_file, cfg))
+ {
+ GNUNET_free (baseconfig);
+ return GNUNET_SYSERR; /* no configuration at all found */
+ }
+ GNUNET_free (baseconfig);
+ if ((filename != NULL) &&
+ (GNUNET_OK != GNUNET_CONFIGURATION_parse (cfg, filename)))
+ {
+ /* specified configuration not found */
+ return GNUNET_SYSERR;
+ }
+ if (((GNUNET_YES !=
+ GNUNET_CONFIGURATION_have_value (cfg, "PATHS", "DEFAULTCONFIG"))) &&
+ (filename != NULL))
+ GNUNET_CONFIGURATION_set_value_string (cfg, "PATHS", "DEFAULTCONFIG",
+ filename);
+ 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/connection.c b/src/util/connection.c
new file mode 100644
index 0000000..8224479
--- /dev/null
+++ b/src/util/connection.c
@@ -0,0 +1,1626 @@
+/*
+ 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/connection.c
+ * @brief TCP connection management
+ * @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.
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_resolver_service.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+/**
+ * Possible functions to call after connect failed or succeeded.
+ */
+enum ConnectContinuations
+{
+ /**
+ * Call nothing.
+ */
+ COCO_NONE = 0,
+
+ /**
+ * Call "receive_again".
+ */
+ COCO_RECEIVE_AGAIN = 1,
+
+ /**
+ * Call "transmit_ready".
+ */
+ COCO_TRANSMIT_READY = 2,
+
+ /**
+ * Call "destroy_continuation".
+ */
+ COCO_DESTROY_CONTINUATION = 4
+};
+
+
+/**
+ * Transmission handle. There can only be one for each connection.
+ */
+struct GNUNET_CONNECTION_TransmitHandle
+{
+
+ /**
+ * Function to call if the send buffer has notify_size
+ * bytes available.
+ */
+ GNUNET_CONNECTION_TransmitReadyNotify notify_ready;
+
+ /**
+ * Closure for notify_ready.
+ */
+ void *notify_ready_cls;
+
+ /**
+ * Our socket handle.
+ */
+ struct GNUNET_CONNECTION_Handle *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;
+
+};
+
+
+/**
+ * During connect, we try multiple possible IP addresses
+ * to find out which one might work.
+ */
+struct AddressProbe
+{
+
+ /**
+ * This is a linked list.
+ */
+ struct AddressProbe *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct AddressProbe *prev;
+
+ /**
+ * The address; do not free (allocated at the end of this struct).
+ */
+ const struct sockaddr *addr;
+
+ /**
+ * Underlying OS's socket.
+ */
+ struct GNUNET_NETWORK_Handle *sock;
+
+ /**
+ * Connection for which we are probing.
+ */
+ struct GNUNET_CONNECTION_Handle *h;
+
+ /**
+ * Lenth of addr.
+ */
+ socklen_t addrlen;
+
+ /**
+ * Task waiting for the socket to finish connecting.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier task;
+};
+
+
+/**
+ * @brief handle for a network socket
+ */
+struct GNUNET_CONNECTION_Handle
+{
+
+ /**
+ * Configuration to use.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Linked list of sockets we are currently trying out
+ * (during connect).
+ */
+ struct AddressProbe *ap_head;
+
+ /**
+ * Linked list of sockets we are currently trying out
+ * (during connect).
+ */
+ struct AddressProbe *ap_tail;
+
+ /**
+ * Network address of the other end-point, may be NULL.
+ */
+ struct sockaddr *addr;
+
+ /**
+ * Pointer to the hostname if socket was
+ * created using DNS lookup, otherwise NULL.
+ */
+ char *hostname;
+
+ /**
+ * Underlying OS's socket, set to NULL after fatal errors.
+ */
+ struct GNUNET_NETWORK_Handle *sock;
+
+ /**
+ * Function to call on data received, NULL if no receive is pending.
+ */
+ GNUNET_CONNECTION_Receiver receiver;
+
+ /**
+ * Closure for receiver.
+ */
+ void *receiver_cls;
+
+ /**
+ * Pointer to our write buffer.
+ */
+ char *write_buffer;
+
+ /**
+ * Current 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 sent).
+ */
+ size_t write_buffer_pos;
+
+ /**
+ * Length of addr.
+ */
+ socklen_t addrlen;
+
+ /**
+ * 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;
+
+ /**
+ * Destroy task (if already scheduled).
+ */
+ GNUNET_SCHEDULER_TaskIdentifier destroy_task;
+
+ /**
+ * Handle to a pending DNS lookup request.
+ */
+ struct GNUNET_RESOLVER_RequestHandle *dns_active;
+
+ /**
+ * The handle we return for GNUNET_CONNECTION_notify_transmit_ready.
+ */
+ struct GNUNET_CONNECTION_TransmitHandle nth;
+
+ /**
+ * Timeout for receiving (in absolute time).
+ */
+ struct GNUNET_TIME_Absolute receive_timeout;
+
+ /**
+ * Functions to call after connect failed or succeeded.
+ */
+ enum ConnectContinuations ccs;
+
+ /**
+ * Maximum number of bytes to read (for receiving).
+ */
+ size_t max;
+
+ /**
+ * Ignore GNUNET_SCHEDULER_REASON_SHUTDOWN for this socket.
+ */
+ int ignore_shutdown;
+
+ /**
+ * Port to connect to.
+ */
+ uint16_t port;
+
+ /**
+ * When shutdown, do not ever actually close the socket, but
+ * free resources. Only should ever be set if using program
+ * termination as a signal (because only then will the leaked
+ * socket be freed!)
+ */
+ int16_t persist;
+
+};
+
+/**
+ * Set the persist option on this connection handle. Indicates
+ * that the underlying socket or fd should never really be closed.
+ * Used for indicating process death.
+ *
+ * @param sock the connection to set persistent
+ */
+void
+GNUNET_CONNECTION_persist_ (struct GNUNET_CONNECTION_Handle *sock)
+{
+ sock->persist = GNUNET_YES;
+}
+
+
+/**
+ * Disable the "CORK" feature for communication with the given socket,
+ * forcing the OS to immediately flush the buffer on transmission
+ * instead of potentially buffering multiple messages. Essentially
+ * reduces the OS send buffers to zero.
+ * Used to make sure that the last messages sent through the connection
+ * reach the other side before the process is terminated.
+ *
+ * @param sock the connection to make flushing and blocking
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_CONNECTION_disable_corking (struct GNUNET_CONNECTION_Handle *sock)
+{
+ return GNUNET_NETWORK_socket_disable_corking (sock->sock);
+}
+
+/**
+ * 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 osSocket existing socket to box
+ * @return the boxed socket handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_existing (struct GNUNET_NETWORK_Handle *osSocket)
+{
+ struct GNUNET_CONNECTION_Handle *ret;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle));
+ ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ ret->write_buffer = GNUNET_malloc (ret->write_buffer_size);
+ ret->sock = osSocket;
+ 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 access function to use to check if access is allowed
+ * @param access_cls closure for access
+ * @param lsock listen socket
+ * @return the socket handle, NULL on error
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_accept (GNUNET_CONNECTION_AccessCheck access,
+ void *access_cls,
+ struct GNUNET_NETWORK_Handle *lsock)
+{
+ struct GNUNET_CONNECTION_Handle *ret;
+ char addr[128];
+ socklen_t addrlen;
+ struct GNUNET_NETWORK_Handle *sock;
+ int aret;
+ struct sockaddr_in *v4;
+ struct sockaddr_in6 *v6;
+ struct sockaddr *sa;
+ void *uaddr;
+ struct GNUNET_CONNECTION_Credentials *gcp;
+ struct GNUNET_CONNECTION_Credentials gc;
+
+#ifdef SO_PEERCRED
+ struct ucred uc;
+ socklen_t olen;
+#endif
+
+ addrlen = sizeof (addr);
+ sock =
+ GNUNET_NETWORK_socket_accept (lsock, (struct sockaddr *) &addr, &addrlen);
+ if (NULL == sock)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "accept");
+ return NULL;
+ }
+ if ((addrlen > sizeof (addr)) || (addrlen < sizeof (sa_family_t)))
+ {
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+ 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;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4->sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+ 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);
+ }
+ gcp = NULL;
+ gc.uid = 0;
+ gc.gid = 0;
+ if (sa->sa_family == AF_UNIX)
+ {
+#if HAVE_GETPEEREID
+ /* most BSDs */
+ if (0 == getpeereid (GNUNET_NETWORK_get_fd (sock), &gc.uid, &gc.gid))
+ gcp = &gc;
+#else
+#ifdef SO_PEERCRED
+ /* largely traditional GNU/Linux */
+ olen = sizeof (uc);
+ if ((0 ==
+ getsockopt (GNUNET_NETWORK_get_fd (sock), SOL_SOCKET, SO_PEERCRED, &uc,
+ &olen)) && (olen == sizeof (uc)))
+ {
+ gc.uid = uc.uid;
+ gc.gid = uc.gid;
+ gcp = &gc;
+ }
+#else
+#if HAVE_GETPEERUCRED
+ /* this is for Solaris 10 */
+ ucred_t *uc;
+
+ uc = NULL;
+ if (0 == getpeerucred (GNUNET_NETWORK_get_fd (sock), &uc))
+ {
+ gc.uid = ucred_geteuid (uc);
+ gc.gid = ucred_getegid (uc);
+ gcp = &gc;
+ }
+ ucred_free (uc);
+#endif
+#endif
+#endif
+ }
+
+ if ((access != NULL) &&
+ (GNUNET_YES != (aret = access (access_cls, gcp, uaddr, addrlen))))
+ {
+ if (aret == GNUNET_NO)
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Access denied to `%s'\n"),
+ GNUNET_a2s (uaddr, addrlen));
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_shutdown (sock, SHUT_RDWR));
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+ GNUNET_free (uaddr);
+ return NULL;
+ }
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle));
+ ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ ret->write_buffer = GNUNET_malloc (ret->write_buffer_size);
+ ret->addr = uaddr;
+ ret->addrlen = addrlen;
+ ret->sock = sock;
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Accepting connection from `%s': %p\n"),
+ GNUNET_a2s (uaddr, addrlen), ret);
+ 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_CONNECTION_get_address (struct GNUNET_CONNECTION_Handle *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;
+}
+
+
+/**
+ * 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).
+ *
+ * @param cls our connection handle
+ * @param tc task context describing why we are here
+ */
+static void
+receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Scheduler let us know that the connect task is finished (or was
+ * cancelled due to shutdown). Now really clean up.
+ *
+ * @param cls our "struct GNUNET_CONNECTION_Handle *"
+ * @param tc unused
+ */
+static void
+destroy_continuation (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CONNECTION_Handle *sock = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+ struct AddressProbe *pos;
+
+ sock->destroy_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_assert (sock->dns_active == NULL);
+ if (0 != (sock->ccs & COCO_TRANSMIT_READY))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Destroy waits for CCS-TR to be done (%p)\n",
+ sock);
+ sock->ccs |= COCO_DESTROY_CONTINUATION;
+ return;
+ }
+ if (sock->write_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Destroy waits for write_task to be done (%p)\n", sock);
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task);
+ sock->destroy_task =
+ GNUNET_SCHEDULER_add_after (sock->write_task, &destroy_continuation,
+ sock);
+ return;
+ }
+ if (0 != (sock->ccs & COCO_RECEIVE_AGAIN))
+ {
+ sock->ccs |= COCO_DESTROY_CONTINUATION;
+ return;
+ }
+ if (sock->sock != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Shutting down socket (%p)\n", sock);
+ if (sock->persist != GNUNET_YES)
+ {
+ if ((GNUNET_YES != GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR))
+ && (errno != ENOTCONN) && (errno != ECONNRESET))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "shutdown");
+ }
+ }
+ if (sock->read_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task);
+ sock->destroy_task =
+ GNUNET_SCHEDULER_add_after (sock->read_task, &destroy_continuation,
+ sock);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Destroy actually runs (%p)!\n", sock);
+ while (NULL != (pos = sock->ap_head))
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock));
+ GNUNET_SCHEDULER_cancel (pos->task);
+ GNUNET_CONTAINER_DLL_remove (sock->ap_head, sock->ap_tail, pos);
+ GNUNET_free (pos);
+ }
+ GNUNET_assert (sock->nth.timeout_task == GNUNET_SCHEDULER_NO_TASK);
+ GNUNET_assert (sock->ccs == COCO_NONE);
+ if (NULL != (notify = sock->nth.notify_ready))
+ {
+ sock->nth.notify_ready = NULL;
+ notify (sock->nth.notify_ready_cls, 0, NULL);
+ }
+
+ if (sock->sock != NULL)
+ {
+ if (sock->persist != GNUNET_YES)
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock));
+ else
+ GNUNET_free (sock->sock); /* at least no memory leak (we deliberately
+ * leak the socket in this special case) ... */
+ }
+ GNUNET_free_non_null (sock->addr);
+ GNUNET_free_non_null (sock->hostname);
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Freeing memory of connection %p.\n", sock);
+ GNUNET_free (sock->write_buffer);
+ GNUNET_free (sock);
+}
+
+
+
+/**
+ * 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.
+ *
+ * @param cls our connection handle
+ * @param tc task context describing why we are here
+ */
+static void
+transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * We've failed for good to establish a connection.
+ *
+ * @param h the connection we tried to establish
+ */
+static void
+connect_fail_continuation (struct GNUNET_CONNECTION_Handle *h)
+{
+ LOG ((0 !=
+ strncmp (h->hostname, "localhost:",
+ 10)) ? GNUNET_ERROR_TYPE_INFO : GNUNET_ERROR_TYPE_WARNING,
+ _
+ ("Failed to establish TCP connection to `%s:%u', no further addresses to try.\n"),
+ h->hostname, h->port);
+ /* connect failed / timed out */
+ GNUNET_break (h->ap_head == NULL);
+ GNUNET_break (h->ap_tail == NULL);
+ GNUNET_break (h->dns_active == GNUNET_NO);
+ GNUNET_break (h->sock == NULL);
+
+ /* trigger jobs that used to wait on "connect_task" */
+ if (0 != (h->ccs & COCO_RECEIVE_AGAIN))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "connect_fail_continuation triggers receive_again (%p)\n", h);
+ h->ccs -= COCO_RECEIVE_AGAIN;
+ h->read_task = GNUNET_SCHEDULER_add_now (&receive_again, h);
+ }
+ if (0 != (h->ccs & COCO_TRANSMIT_READY))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "connect_fail_continuation cancels timeout_task, triggers transmit_ready (%p)\n",
+ h);
+ GNUNET_assert (h->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK);
+ GNUNET_SCHEDULER_cancel (h->nth.timeout_task);
+ h->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ h->ccs -= COCO_TRANSMIT_READY;
+ GNUNET_assert (h->nth.notify_ready != NULL);
+ GNUNET_assert (h->write_task == GNUNET_SCHEDULER_NO_TASK);
+ h->write_task = GNUNET_SCHEDULER_add_now (&transmit_ready, h);
+ }
+ if (0 != (h->ccs & COCO_DESTROY_CONTINUATION))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "connect_fail_continuation runs destroy_continuation (%p)\n", h);
+ h->ccs -= COCO_DESTROY_CONTINUATION;
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->destroy_task);
+ h->destroy_task = GNUNET_SCHEDULER_add_now (&destroy_continuation, h);
+ }
+}
+
+
+/**
+ * We've succeeded in establishing a connection.
+ *
+ * @param h the connection we tried to establish
+ */
+static void
+connect_success_continuation (struct GNUNET_CONNECTION_Handle *h)
+{
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Connection to `%s' succeeded! (%p)\n",
+ GNUNET_a2s (h->addr, h->addrlen), h);
+ /* trigger jobs that waited for the connection */
+ if (0 != (h->ccs & COCO_RECEIVE_AGAIN))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "connect_success_continuation runs receive_again (%p)\n", h);
+ h->ccs -= COCO_RECEIVE_AGAIN;
+ h->read_task = GNUNET_SCHEDULER_add_now (&receive_again, h);
+ }
+ if (0 != (h->ccs & COCO_TRANSMIT_READY))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "connect_success_continuation runs transmit_ready, cancels timeout_task (%p)\n",
+ h);
+ GNUNET_assert (h->nth.timeout_task != GNUNET_SCHEDULER_NO_TASK);
+ GNUNET_SCHEDULER_cancel (h->nth.timeout_task);
+ h->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ h->ccs -= COCO_TRANSMIT_READY;
+ GNUNET_assert (h->write_task == GNUNET_SCHEDULER_NO_TASK);
+ GNUNET_assert (h->nth.notify_ready != NULL);
+ h->write_task =
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
+ (h->nth.transmit_timeout), h->sock,
+ &transmit_ready, h);
+ }
+ if (0 != (h->ccs & COCO_DESTROY_CONTINUATION))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "connect_success_continuation runs destroy_continuation (%p)\n", h);
+ h->ccs -= COCO_DESTROY_CONTINUATION;
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->destroy_task);
+ h->destroy_task = GNUNET_SCHEDULER_add_now (&destroy_continuation, h);
+ }
+}
+
+
+/**
+ * Scheduler let us know that we're either ready to write on the
+ * socket OR connect timed out. Do the right thing.
+ *
+ * @param cls the "struct AddressProbe*" with the address that we are probing
+ * @param tc success or failure info about the connect attempt.
+ */
+static void
+connect_probe_continuation (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct AddressProbe *ap = cls;
+ struct GNUNET_CONNECTION_Handle *h = ap->h;
+ struct AddressProbe *pos;
+ int error;
+ socklen_t len;
+
+ GNUNET_assert (ap->sock != NULL);
+ GNUNET_CONTAINER_DLL_remove (h->ap_head, h->ap_tail, ap);
+ len = sizeof (error);
+ errno = 0;
+ error = 0;
+ if ((0 == (tc->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) ||
+ (GNUNET_OK !=
+ GNUNET_NETWORK_socket_getsockopt (ap->sock, SOL_SOCKET, SO_ERROR, &error,
+ &len)) || (error != 0))
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock));
+ GNUNET_free (ap);
+ if ((NULL == h->ap_head) && (h->dns_active == GNUNET_NO))
+ connect_fail_continuation (h);
+ return;
+ }
+ GNUNET_assert (h->sock == NULL);
+ h->sock = ap->sock;
+ GNUNET_assert (h->addr == NULL);
+ h->addr = GNUNET_malloc (ap->addrlen);
+ memcpy (h->addr, ap->addr, ap->addrlen);
+ h->addrlen = ap->addrlen;
+ GNUNET_free (ap);
+ /* cancel all other attempts */
+ while (NULL != (pos = h->ap_head))
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (pos->sock));
+ GNUNET_SCHEDULER_cancel (pos->task);
+ GNUNET_CONTAINER_DLL_remove (h->ap_head, h->ap_tail, pos);
+ GNUNET_free (pos);
+ }
+ connect_success_continuation (h);
+}
+
+
+/**
+ * Try to establish a socket connection given the specified address.
+ * This function is called by the resolver once we have a DNS reply.
+ *
+ * @param cls our "struct GNUNET_CONNECTION_Handle *"
+ * @param addr address to try, NULL for "last call"
+ * @param addrlen length of addr
+ */
+static void
+try_connect_using_address (void *cls, const struct sockaddr *addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_CONNECTION_Handle *h = cls;
+ struct AddressProbe *ap;
+ struct GNUNET_TIME_Relative delay;
+
+ if (addr == NULL)
+ {
+ h->dns_active = NULL;
+ if ((NULL == h->ap_head) && (NULL == h->sock))
+ connect_fail_continuation (h);
+ return;
+ }
+ if (h->sock != NULL)
+ return; /* already connected */
+ GNUNET_assert (h->addr == NULL);
+ /* try to connect */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to connect using address `%s:%u/%s:%u'\n", h->hostname, h->port,
+ GNUNET_a2s (addr, addrlen), h->port);
+ ap = GNUNET_malloc (sizeof (struct AddressProbe) + addrlen);
+ ap->addr = (const struct sockaddr *) &ap[1];
+ memcpy (&ap[1], addr, addrlen);
+ ap->addrlen = addrlen;
+ ap->h = h;
+
+ switch (ap->addr->sa_family)
+ {
+ case AF_INET:
+ ((struct sockaddr_in *) ap->addr)->sin_port = htons (h->port);
+ break;
+ case AF_INET6:
+ ((struct sockaddr_in6 *) ap->addr)->sin6_port = htons (h->port);
+ break;
+ default:
+ GNUNET_break (0);
+ GNUNET_free (ap);
+ return; /* not supported by us */
+ }
+ ap->sock = GNUNET_NETWORK_socket_create (ap->addr->sa_family, SOCK_STREAM, 0);
+ if (ap->sock == NULL)
+ {
+ GNUNET_free (ap);
+ return; /* not supported by OS */
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Trying to connect to `%s' (%p)\n"),
+ GNUNET_a2s (ap->addr, ap->addrlen), h);
+ if ((GNUNET_OK !=
+ GNUNET_NETWORK_socket_connect (ap->sock, ap->addr, ap->addrlen)) &&
+ (errno != EINPROGRESS))
+ {
+ /* maybe refused / unsupported address, try next */
+ LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect");
+#if 0
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Failed to connect to `%s' (%p)\n"),
+ GNUNET_a2s (ap->addr, ap->addrlen), h);
+#endif
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ap->sock));
+ GNUNET_free (ap);
+ return;
+ }
+ GNUNET_CONTAINER_DLL_insert (h->ap_head, h->ap_tail, ap);
+ delay = GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT;
+ if (h->nth.notify_ready != NULL)
+ delay =
+ GNUNET_TIME_relative_min (delay,
+ GNUNET_TIME_absolute_get_remaining (h->
+ nth.transmit_timeout));
+ if (h->receiver != NULL)
+ delay =
+ GNUNET_TIME_relative_min (delay,
+ GNUNET_TIME_absolute_get_remaining
+ (h->receive_timeout));
+ ap->task =
+ GNUNET_SCHEDULER_add_write_net (delay, ap->sock,
+ &connect_probe_continuation, ap);
+}
+
+
+/**
+ * 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 cfg configuration to use
+ * @param hostname name of the host to connect to
+ * @param port port to connect to
+ * @return the socket handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_connect (const struct GNUNET_CONFIGURATION_Handle
+ *cfg, const char *hostname,
+ uint16_t port)
+{
+ struct GNUNET_CONNECTION_Handle *ret;
+
+ GNUNET_assert (0 < strlen (hostname)); /* sanity check */
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle));
+ ret->cfg = cfg;
+ ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ ret->write_buffer = GNUNET_malloc (ret->write_buffer_size);
+ ret->port = port;
+ ret->hostname = GNUNET_strdup (hostname);
+ ret->dns_active =
+ GNUNET_RESOLVER_ip_get (ret->hostname, AF_UNSPEC,
+ GNUNET_CONNECTION_CONNECT_RETRY_TIMEOUT,
+ &try_connect_using_address, ret);
+ return ret;
+}
+
+
+/**
+ * Create a socket handle by connecting to a UNIX domain service.
+ * This function returns immediately, even if the connection has not
+ * yet been established. This function only creates UNIX connections.
+ *
+ * @param cfg configuration to use
+ * @param unixpath path to connect to
+ * @return the socket handle, NULL on systems without UNIX support
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_connect_to_unixpath (const struct
+ GNUNET_CONFIGURATION_Handle
+ *cfg, const char *unixpath)
+{
+#ifdef AF_UNIX
+ struct GNUNET_CONNECTION_Handle *ret;
+ struct sockaddr_un *un;
+ size_t slen;
+
+ GNUNET_assert (0 < strlen (unixpath)); /* sanity check */
+ un = GNUNET_malloc (sizeof (struct sockaddr_un));
+ un->sun_family = AF_UNIX;
+ slen = strlen (unixpath);
+ if (slen >= sizeof (un->sun_path))
+ slen = sizeof (un->sun_path) - 1;
+ memcpy (un->sun_path, unixpath, slen);
+ un->sun_path[slen] = '\0';
+ slen = sizeof (struct sockaddr_un);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ un->sun_len = (u_char) slen;
+#endif
+#if LINUX
+ un->sun_path[0] = '\0';
+#endif
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CONNECTION_Handle));
+ ret->cfg = cfg;
+ ret->write_buffer_size = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ ret->write_buffer = GNUNET_malloc (ret->write_buffer_size);
+ ret->port = 0;
+ ret->hostname = NULL;
+ ret->addr = (struct sockaddr *) un;
+ ret->addrlen = slen;
+ ret->sock = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
+ if (NULL == ret->sock)
+ {
+ GNUNET_free (ret->addr);
+ GNUNET_free (ret->write_buffer);
+ GNUNET_free (ret);
+ return NULL;
+ }
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_connect (ret->sock, ret->addr, ret->addrlen))
+ {
+ /* Just return; we expect everything to work eventually so don't fail HARD */
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret->sock));
+ ret->sock = NULL;
+ return ret;
+ }
+ connect_success_continuation (ret);
+ return ret;
+#else
+ return NULL;
+#endif
+}
+
+
+/**
+ * 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 af_family address family to use
+ * @param serv_addr server address
+ * @param addrlen length of server address
+ * @return the socket handle
+ */
+struct GNUNET_CONNECTION_Handle *
+GNUNET_CONNECTION_create_from_sockaddr (int af_family,
+ const struct sockaddr *serv_addr,
+ socklen_t addrlen)
+{
+ struct GNUNET_NETWORK_Handle *s;
+ struct GNUNET_CONNECTION_Handle *ret;
+
+
+ s = GNUNET_NETWORK_socket_create (af_family, SOCK_STREAM, 0);
+ if (s == NULL)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "socket");
+ return NULL;
+ }
+ if ((GNUNET_OK != GNUNET_NETWORK_socket_connect (s, serv_addr, addrlen)) &&
+ (errno != EINPROGRESS))
+ {
+ /* maybe refused / unsupported address, try next */
+ LOG_STRERROR (GNUNET_ERROR_TYPE_INFO, "connect");
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Attempt to connect to `%s' failed\n"),
+ GNUNET_a2s (serv_addr, addrlen));
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (s));
+ return NULL;
+ }
+ ret = GNUNET_CONNECTION_create_from_existing (s);
+ ret->addr = GNUNET_malloc (addrlen);
+ memcpy (ret->addr, serv_addr, addrlen);
+ ret->addrlen = addrlen;
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Trying to connect to `%s' (%p)\n"),
+ GNUNET_a2s (serv_addr, addrlen), ret);
+ return ret;
+}
+
+
+/**
+ * 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_CONNECTION_check (struct GNUNET_CONNECTION_Handle *sock)
+{
+ if ((sock->ap_head != NULL) || (sock->dns_active != NULL))
+ return GNUNET_YES; /* still trying to connect */
+ return (sock->sock == NULL) ? GNUNET_NO : GNUNET_YES;
+}
+
+
+/**
+ * Close the socket and free associated resources. Pending
+ * transmissions may be completed or dropped depending on the
+ * arguments. If a receive call is pending and should
+ * NOT be completed, 'GNUNET_CONNECTION_receive_cancel'
+ * should be called explicitly first.
+ *
+ * @param sock socket to destroy
+ * @param finish_pending_write should pending writes be completed or aborted?
+ * (this applies to transmissions where the data has already been
+ * read from the application; all other transmissions should be
+ * aborted using 'GNUNET_CONNECTION_notify_transmit_ready_cancel').
+ */
+void
+GNUNET_CONNECTION_destroy (struct GNUNET_CONNECTION_Handle *sock,
+ int finish_pending_write)
+{
+ if (GNUNET_NO == finish_pending_write)
+ {
+ if (sock->write_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sock->write_task);
+ sock->write_task = GNUNET_SCHEDULER_NO_TASK;
+ sock->write_buffer_off = 0;
+ }
+ sock->nth.notify_ready = NULL;
+ }
+ if ((sock->write_buffer_off == 0) && (sock->dns_active != NULL))
+ {
+ GNUNET_RESOLVER_request_cancel (sock->dns_active);
+ sock->dns_active = NULL;
+ }
+
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->destroy_task);
+ sock->destroy_task = GNUNET_SCHEDULER_add_now (&destroy_continuation, sock);
+}
+
+
+/**
+ * Tell the receiver callback that a timeout was reached.
+ */
+static void
+signal_timeout (struct GNUNET_CONNECTION_Handle *sh)
+{
+ GNUNET_CONNECTION_Receiver receiver;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Network signals time out to receiver (%p)!\n",
+ sh);
+ 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_CONNECTION_Handle *sh, int errcode)
+{
+ GNUNET_CONNECTION_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_CONNECTION_Handle *sh = cls;
+ struct GNUNET_TIME_Absolute now;
+ char buffer[sh->max];
+ ssize_t ret;
+ GNUNET_CONNECTION_Receiver receiver;
+
+ sh->read_task = GNUNET_SCHEDULER_NO_TASK;
+ if ((GNUNET_YES == sh->ignore_shutdown) &&
+ (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
+ {
+ /* ignore shutdown request, go again immediately */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Ignoring shutdown signal per configuration\n");
+ sh->read_task =
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_absolute_get_remaining
+ (sh->receive_timeout), sh->sock,
+ &receive_ready, sh);
+ return;
+ }
+ now = GNUNET_TIME_absolute_get ();
+ if ((now.abs_value > sh->receive_timeout.abs_value) ||
+ (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT)) ||
+ (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
+ {
+ if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive from `%s' encounters error: time out by %llums... (%p)\n",
+ GNUNET_a2s (sh->addr, sh->addrlen),
+ GNUNET_TIME_absolute_get_duration (sh->receive_timeout).rel_value,
+ sh);
+ signal_timeout (sh);
+ return;
+ }
+ if (sh->sock == NULL)
+ {
+ /* connect failed for good */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive encounters error, socket closed... (%p)\n", sh);
+ signal_error (sh, ECONNREFUSED);
+ return;
+ }
+ GNUNET_assert (GNUNET_NETWORK_fdset_isset (tc->read_ready, sh->sock));
+RETRY:
+ ret = GNUNET_NETWORK_socket_recv (sh->sock, buffer, sh->max);
+ if (ret == -1)
+ {
+ if (errno == EINTR)
+ goto RETRY;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Error receiving: %s\n", STRERROR (errno));
+ signal_error (sh, errno);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "receive_ready read %u/%u bytes from `%s' (%p)!\n", (unsigned int) ret,
+ sh->max, GNUNET_a2s (sh->addr, sh->addrlen), sh);
+ 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).
+ *
+ * @param cls our connection handle
+ * @param tc task context describing why we are here
+ */
+static void
+receive_again (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CONNECTION_Handle *sh = cls;
+ struct GNUNET_TIME_Absolute now;
+
+ sh->read_task = GNUNET_SCHEDULER_NO_TASK;
+ if (sh->sock == NULL)
+ {
+ /* not connected and no longer trying */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive encounters error, socket closed (%p)...\n", sh);
+ signal_error (sh, ECONNREFUSED);
+ return;
+ }
+ now = GNUNET_TIME_absolute_get ();
+ if ((now.abs_value > sh->receive_timeout.abs_value) ||
+ (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive encounters error: time out (%p)...\n", sh);
+ signal_timeout (sh);
+ return;
+ }
+ GNUNET_assert (sh->sock != NULL);
+ /* connect succeeded, wait for data! */
+ sh->read_task =
+ GNUNET_SCHEDULER_add_read_net (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 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
+ */
+void
+GNUNET_CONNECTION_receive (struct GNUNET_CONNECTION_Handle *sock, size_t max,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_CONNECTION_Receiver receiver,
+ void *receiver_cls)
+{
+ struct GNUNET_SCHEDULER_TaskContext tc;
+
+ GNUNET_assert ((sock->read_task == GNUNET_SCHEDULER_NO_TASK) &&
+ (0 == (sock->ccs & COCO_RECEIVE_AGAIN)) &&
+ (sock->receiver == NULL));
+ sock->receiver = receiver;
+ sock->receiver_cls = receiver_cls;
+ sock->receive_timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ sock->max = max;
+ if (sock->sock != NULL)
+ {
+ memset (&tc, 0, sizeof (tc));
+ tc.reason = GNUNET_SCHEDULER_REASON_PREREQ_DONE;
+ receive_again (sock, &tc);
+ return;
+ }
+ if ((sock->dns_active == NULL) && (sock->ap_head == NULL))
+ {
+ receiver (receiver_cls, NULL, 0, NULL, 0, ETIMEDOUT);
+ return;
+ }
+ sock->ccs += COCO_RECEIVE_AGAIN;
+}
+
+
+/**
+ * Configure this connection to ignore shutdown signals.
+ *
+ * @param sock socket handle
+ * @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default
+ */
+void
+GNUNET_CONNECTION_ignore_shutdown (struct GNUNET_CONNECTION_Handle *sock,
+ int do_ignore)
+{
+ sock->ignore_shutdown = do_ignore;
+}
+
+
+/**
+ * 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
+ * @return closure of the original receiver callback closure
+ */
+void *
+GNUNET_CONNECTION_receive_cancel (struct GNUNET_CONNECTION_Handle *sock)
+{
+ if (sock->read_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_assert (sock == GNUNET_SCHEDULER_cancel (sock->read_task));
+ sock->read_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ else
+ {
+ GNUNET_assert (0 != (sock->ccs & COCO_RECEIVE_AGAIN));
+ sock->ccs -= COCO_RECEIVE_AGAIN;
+ }
+ 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_CONNECTION_Handle *sock)
+{
+ size_t used;
+ size_t avail;
+ size_t size;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ GNUNET_assert (sock->write_task == GNUNET_SCHEDULER_NO_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 (size > avail)
+ return GNUNET_NO;
+ sock->nth.notify_ready = NULL;
+ 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;
+ }
+ avail = sock->write_buffer_size - sock->write_buffer_off;
+ GNUNET_assert (avail >= size);
+ size =
+ notify (sock->nth.notify_ready_cls, avail,
+ &sock->write_buffer[sock->write_buffer_off]);
+ GNUNET_assert (size <= avail);
+ 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.
+ *
+ * @param cls the 'struct GNUNET_CONNECTION_Handle'
+ * @param tc scheduler context
+ */
+static void
+transmit_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CONNECTION_Handle *sock = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ sock->nth.timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmit to `%s:%u/%s' fails, time out reached (%p).\n",
+ sock->hostname,
+ sock->port, GNUNET_a2s (sock->addr, sock->addrlen), sock);
+ GNUNET_assert (0 != (sock->ccs & COCO_TRANSMIT_READY));
+ sock->ccs -= COCO_TRANSMIT_READY; /* remove request */
+ notify = sock->nth.notify_ready;
+ sock->nth.notify_ready = NULL;
+ notify (sock->nth.notify_ready_cls, 0, NULL);
+}
+
+
+/**
+ * Task invoked by the scheduler when we failed to connect
+ * at the time of being asked to transmit.
+ *
+ * This task notifies the client about the error.
+ *
+ * @param cls the 'struct GNUNET_CONNECTION_Handle'
+ * @param tc scheduler context
+ */
+static void
+connect_error (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CONNECTION_Handle *sock = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmission request of size %u fails (%s/%u), connection failed (%p).\n",
+ sock->nth.notify_size, sock->hostname, sock->port, sock);
+ sock->write_task = GNUNET_SCHEDULER_NO_TASK;
+ notify = sock->nth.notify_ready;
+ sock->nth.notify_ready = NULL;
+ notify (sock->nth.notify_ready_cls, 0, NULL);
+}
+
+
+/**
+ * FIXME
+ *
+ * @param sock FIXME
+ */
+static void
+transmit_error (struct GNUNET_CONNECTION_Handle *sock)
+{
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+
+ if (NULL != sock->sock)
+ {
+ GNUNET_NETWORK_socket_shutdown (sock->sock, SHUT_RDWR);
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock->sock));
+ sock->sock = NULL;
+ }
+ if (sock->read_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (sock->read_task);
+ sock->read_task = GNUNET_SCHEDULER_NO_TASK;
+ signal_timeout (sock);
+ return;
+ }
+ if (sock->nth.notify_ready == NULL)
+ return; /* nobody to tell about it */
+ notify = sock->nth.notify_ready;
+ sock->nth.notify_ready = NULL;
+ notify (sock->nth.notify_ready_cls, 0, 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.
+ *
+ * @param cls our connection handle
+ * @param tc task context describing why we are here
+ */
+static void
+transmit_ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_CONNECTION_Handle *sock = cls;
+ GNUNET_CONNECTION_TransmitReadyNotify notify;
+ ssize_t ret;
+ size_t have;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "transmit_ready running (%p).\n", sock);
+ GNUNET_assert (sock->write_task != GNUNET_SCHEDULER_NO_TASK);
+ sock->write_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_assert (sock->nth.timeout_task == GNUNET_SCHEDULER_NO_TASK);
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+ if ((sock->ignore_shutdown == GNUNET_YES) && (NULL != sock->sock))
+ goto SCHEDULE_WRITE; /* ignore shutdown, go again immediately */
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmit to `%s' fails, shutdown happened (%p).\n",
+ GNUNET_a2s (sock->addr, sock->addrlen), sock);
+ notify = sock->nth.notify_ready;
+ if (NULL != notify)
+ {
+ sock->nth.notify_ready = NULL;
+ notify (sock->nth.notify_ready_cls, 0, NULL);
+ }
+ return;
+ }
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmit to `%s' fails, time out reached (%p).\n",
+ GNUNET_a2s (sock->addr, sock->addrlen), sock);
+ notify = sock->nth.notify_ready;
+ GNUNET_assert (NULL != notify);
+ sock->nth.notify_ready = NULL;
+ notify (sock->nth.notify_ready_cls, 0, NULL);
+ return;
+ }
+ GNUNET_assert (NULL != sock->sock);
+ if (tc->write_ready == NULL)
+ {
+ /* special circumstances (in particular,
+ * PREREQ_DONE after connect): not yet ready to write,
+ * but no "fatal" error either. Hence retry. */
+ goto SCHEDULE_WRITE;
+ }
+ if (!GNUNET_NETWORK_fdset_isset (tc->write_ready, sock->sock))
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _
+ ("Could not satisfy pending transmission request, socket closed or connect failed (%p).\n"),
+ sock);
+ transmit_error (sock);
+ return; /* connect failed for good, we're finished */
+ }
+ GNUNET_assert (sock->write_buffer_off >= sock->write_buffer_pos);
+ if ((sock->nth.notify_ready != NULL) &&
+ (sock->write_buffer_size < sock->nth.notify_size))
+ {
+ sock->write_buffer =
+ GNUNET_realloc (sock->write_buffer, sock->nth.notify_size);
+ sock->write_buffer_size = sock->nth.notify_size;
+ }
+ process_notify (sock);
+ have = sock->write_buffer_off - sock->write_buffer_pos;
+ if (have == 0)
+ {
+ /* no data ready for writing, terminate write loop */
+ return;
+ }
+ GNUNET_assert (have <= sock->write_buffer_size);
+ GNUNET_assert (have + sock->write_buffer_pos <= sock->write_buffer_size);
+ GNUNET_assert (sock->write_buffer_pos <= sock->write_buffer_size);
+RETRY:
+ ret =
+ GNUNET_NETWORK_socket_send (sock->sock,
+ &sock->write_buffer[sock->write_buffer_pos],
+ have);
+ if (ret == -1)
+ {
+ if (errno == EINTR)
+ goto RETRY;
+ LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, "send");
+ transmit_error (sock);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "transmit_ready transmitted %u/%u bytes to `%s' (%p)\n",
+ (unsigned int) ret, have, GNUNET_a2s (sock->addr, sock->addrlen), sock);
+ 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 ((sock->write_buffer_off == 0) && (NULL == sock->nth.notify_ready))
+ return; /* all data sent! */
+ /* not done writing, schedule more */
+SCHEDULE_WRITE:
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Re-scheduling transmit_ready (more to do) (%p).\n", sock);
+ have = sock->write_buffer_off - sock->write_buffer_pos;
+ GNUNET_assert ((sock->nth.notify_ready != NULL) || (have > 0));
+ if (sock->write_task == GNUNET_SCHEDULER_NO_TASK)
+ sock->write_task =
+ GNUNET_SCHEDULER_add_write_net ((sock->nth.notify_ready ==
+ NULL) ? GNUNET_TIME_UNIT_FOREVER_REL :
+ GNUNET_TIME_absolute_get_remaining
+ (sock->nth.transmit_timeout),
+ 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_CONNECTION_TransmitHandle *
+GNUNET_CONNECTION_notify_transmit_ready (struct GNUNET_CONNECTION_Handle *sock,
+ size_t size,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_CONNECTION_TransmitReadyNotify
+ notify, void *notify_cls)
+{
+ if (sock->nth.notify_ready != NULL)
+ {
+ GNUNET_assert (0);
+ return NULL;
+ }
+ GNUNET_assert (notify != NULL);
+ GNUNET_assert (size < GNUNET_SERVER_MAX_MESSAGE_SIZE);
+ 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);
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == sock->nth.timeout_task);
+ if ((sock->sock == NULL) && (sock->ap_head == NULL) &&
+ (sock->dns_active == NULL))
+ {
+ if (sock->write_task != GNUNET_SCHEDULER_NO_TASK)
+ GNUNET_SCHEDULER_cancel (sock->write_task);
+ sock->write_task = GNUNET_SCHEDULER_add_now (&connect_error, sock);
+ return &sock->nth;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != sock->write_task)
+ return &sock->nth;
+ if (sock->sock != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Scheduling transmit_ready (%p).\n", sock);
+ sock->write_task =
+ GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_absolute_get_remaining
+ (sock->nth.transmit_timeout),
+ sock->sock, &transmit_ready, sock);
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "CCS-Scheduling transmit_ready, adding timeout task (%p).\n", sock);
+ sock->ccs |= COCO_TRANSMIT_READY;
+ sock->nth.timeout_task =
+ GNUNET_SCHEDULER_add_delayed (timeout, &transmit_timeout, sock);
+ }
+ return &sock->nth;
+}
+
+
+/**
+ * Cancel the specified transmission-ready notification.
+ *
+ * @param th notification to cancel
+ */
+void
+GNUNET_CONNECTION_notify_transmit_ready_cancel (struct
+ GNUNET_CONNECTION_TransmitHandle
+ *th)
+{
+ GNUNET_assert (th->notify_ready != NULL);
+ if (0 != (th->sh->ccs & COCO_TRANSMIT_READY))
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "notify_transmit_ready_cancel cancels timeout_task (%p)\n", th);
+ GNUNET_SCHEDULER_cancel (th->timeout_task);
+ th->timeout_task = GNUNET_SCHEDULER_NO_TASK;
+ th->sh->ccs -= COCO_TRANSMIT_READY;
+ }
+ else
+ {
+ if (th->sh->write_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (th->sh->write_task);
+ th->sh->write_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ }
+ th->notify_ready = NULL;
+}
+
+/* end of connection.c */
diff --git a/src/util/container_bloomfilter.c b/src/util/container_bloomfilter.c
new file mode 100644
index 0000000..84aab6b
--- /dev/null
+++ b/src/util/container_bloomfilter.c
@@ -0,0 +1,858 @@
+/*
+ This file is part of GNUnet.
+ (C) 2001, 2002, 2003, 2004, 2006, 2008, 2011 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"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+struct GNUNET_CONTAINER_BloomFilter
+{
+
+ /**
+ * The actual bloomfilter bit array
+ */
+ char *bitArray;
+
+ /**
+ * Filename of the filter
+ */
+ char *filename;
+
+ /**
+ * The bit counter file on disk
+ */
+ struct GNUNET_DISK_FileHandle *fh;
+
+ /**
+ * How many bits we set for each stored element
+ */
+ unsigned int addressesPerElement;
+
+ /**
+ * Size of bitArray in bytes
+ */
+ size_t bitArraySize;
+
+};
+
+
+
+/**
+ * Get size of the bloom filter.
+ *
+ * @param bf the filter
+ * @return number of bytes used for the data of the bloom filter
+ */
+size_t
+GNUNET_CONTAINER_bloomfilter_get_size (const struct GNUNET_CONTAINER_BloomFilter
+ *bf)
+{
+ if (bf == NULL)
+ return 0;
+ return bf->bitArraySize;
+}
+
+
+/**
+ * Copy an existing memory. Any association with a file
+ * on-disk will be lost in the process.
+ * @param bf the filter to copy
+ * @return copy of the bf
+ */
+struct GNUNET_CONTAINER_BloomFilter *
+GNUNET_CONTAINER_bloomfilter_copy (const struct GNUNET_CONTAINER_BloomFilter
+ *bf)
+{
+ return GNUNET_CONTAINER_bloomfilter_init (bf->bitArray, bf->bitArraySize,
+ bf->addressesPerElement);
+}
+
+
+/**
+ * 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)
+{
+ size_t 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)
+{
+ size_t 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)
+{
+ size_t 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 fh A file to keep the 4 bit address usage counters in
+ */
+static void
+incrementBit (char *bitArray, unsigned int bitIdx,
+ const struct GNUNET_DISK_FileHandle *fh)
+{
+ OFF_T fileSlot;
+ unsigned char value;
+ unsigned int high;
+ unsigned int low;
+ unsigned int targetLoc;
+
+ setBit (bitArray, bitIdx);
+ if (GNUNET_DISK_handle_invalid (fh))
+ return;
+ /* Update the counter file on disk */
+ fileSlot = bitIdx / 2;
+ targetLoc = bitIdx % 2;
+
+ GNUNET_assert (fileSlot ==
+ GNUNET_DISK_file_seek (fh, fileSlot, GNUNET_DISK_SEEK_SET));
+ if (1 != GNUNET_DISK_file_read (fh, &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 ==
+ GNUNET_DISK_file_seek (fh, fileSlot, GNUNET_DISK_SEEK_SET));
+ GNUNET_assert (1 == GNUNET_DISK_file_write (fh, &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 fh A file to keep the 4bit address usage counters in
+ */
+static void
+decrementBit (char *bitArray, unsigned int bitIdx,
+ const struct GNUNET_DISK_FileHandle *fh)
+{
+ OFF_T fileSlot;
+ unsigned char value;
+ unsigned int high;
+ unsigned int low;
+ unsigned int targetLoc;
+
+ if (GNUNET_DISK_handle_invalid (fh))
+ return; /* cannot decrement! */
+ /* Each char slot in the counter file holds two 4 bit counters */
+ fileSlot = bitIdx / 2;
+ targetLoc = bitIdx % 2;
+ GNUNET_DISK_file_seek (fh, fileSlot, GNUNET_DISK_SEEK_SET);
+ if (1 != GNUNET_DISK_file_read (fh, &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);
+ GNUNET_DISK_file_seek (fh, fileSlot, GNUNET_DISK_SEEK_SET);
+ GNUNET_assert (1 == GNUNET_DISK_file_write (fh, &value, 1));
+}
+
+#define BUFFSIZE 65536
+
+/**
+ * Creates a file filled with zeroes
+ *
+ * @param fh the file handle
+ * @param size the size of the file
+ * @return GNUNET_OK if created ok, GNUNET_SYSERR otherwise
+ */
+static int
+make_empty_file (const struct GNUNET_DISK_FileHandle *fh, size_t size)
+{
+ char buffer[BUFFSIZE];
+ size_t bytesleft = size;
+ int res = 0;
+
+ if (GNUNET_DISK_handle_invalid (fh))
+ return GNUNET_SYSERR;
+ memset (buffer, 0, sizeof (buffer));
+ GNUNET_DISK_file_seek (fh, 0, GNUNET_DISK_SEEK_SET);
+ while (bytesleft > 0)
+ {
+ if (bytesleft > sizeof (buffer))
+ {
+ res = GNUNET_DISK_file_write (fh, buffer, sizeof (buffer));
+ if (res >= 0)
+ bytesleft -= res;
+ }
+ else
+ {
+ res = GNUNET_DISK_file_write (fh, buffer, bytesleft);
+ if (res >= 0)
+ bytesleft -= res;
+ }
+ if (GNUNET_SYSERR == res)
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+/* ************** GNUNET_CONTAINER_BloomFilter 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 cls closure
+ * @param bf the filter to manipulate
+ * @param bit the current bit
+ * @return GNUNET_YES to continue, GNUNET_NO to stop early
+ */
+typedef int (*BitIterator) (void *cls,
+ const struct GNUNET_CONTAINER_BloomFilter * bf,
+ unsigned int bit);
+
+
+/**
+ * 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 (const struct GNUNET_CONTAINER_BloomFilter *bf,
+ BitIterator callback, void *arg, const GNUNET_HashCode * key)
+{
+ GNUNET_HashCode tmp[2];
+ int bitCount;
+ unsigned int round;
+ unsigned int slot = 0;
+
+ bitCount = bf->addressesPerElement;
+ tmp[0] = *key;
+ round = 0;
+ while (bitCount > 0)
+ {
+ while (slot < (sizeof (GNUNET_HashCode) / sizeof (uint32_t)))
+ {
+ if (GNUNET_YES !=
+ callback (arg, bf,
+ (((uint32_t *) & tmp[round & 1])[slot]) &
+ ((bf->bitArraySize * 8) - 1)))
+ return;
+ 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 cls pointer to writeable form of bf
+ * @param bf the filter to manipulate
+ * @param bit the bit to increment
+ * @return GNUNET_YES
+ */
+static int
+incrementBitCallback (void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf,
+ unsigned int bit)
+{
+ struct GNUNET_CONTAINER_BloomFilter *b = cls;
+
+ incrementBit (b->bitArray, bit, bf->fh);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Callback: decrement bit
+ *
+ * @param cls pointer to writeable form of bf
+ * @param bf the filter to manipulate
+ * @param bit the bit to decrement
+ * @return GNUNET_YES
+ */
+static int
+decrementBitCallback (void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf,
+ unsigned int bit)
+{
+ struct GNUNET_CONTAINER_BloomFilter *b = cls;
+
+ decrementBit (b->bitArray, bit, bf->fh);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Callback: test if all bits are set
+ *
+ * @param cls pointer set to GNUNET_NO if bit is not set
+ * @param bf the filter
+ * @param bit the bit to test
+ * @return YES if the bit is set, NO if not
+ */
+static int
+testBitCallback (void *cls, const struct GNUNET_CONTAINER_BloomFilter *bf,
+ unsigned int bit)
+{
+ int *arg = cls;
+
+ if (GNUNET_NO == testBit (bf->bitArray, bit))
+ {
+ *arg = GNUNET_NO;
+ return GNUNET_NO;
+ }
+ return GNUNET_YES;
+}
+
+/* *********************** 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, size_t size,
+ unsigned int k)
+{
+ struct GNUNET_CONTAINER_BloomFilter *bf;
+ char *rbuff;
+ OFF_T pos;
+ int i;
+ size_t ui;
+ OFF_T fsize;
+ int must_read;
+
+ GNUNET_assert (NULL != filename);
+ if ((k == 0) || (size == 0))
+ return NULL;
+ if (size < BUFFSIZE)
+ size = BUFFSIZE;
+ ui = 1;
+ while ( (ui < size) &&
+ (ui * 2 > ui) )
+ 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 (GNUNET_YES == GNUNET_DISK_file_test (filename))
+ bf->fh =
+ GNUNET_DISK_file_open (filename,
+ GNUNET_DISK_OPEN_READWRITE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ if (NULL != bf->fh)
+ {
+ /* file existed, try to read it! */
+ must_read = GNUNET_YES;
+ if (GNUNET_OK !=
+ GNUNET_DISK_file_handle_size (bf->fh, &fsize))
+ {
+ GNUNET_DISK_file_close (bf->fh);
+ GNUNET_free (bf);
+ return NULL;
+ }
+ if (fsize == 0)
+ {
+ /* found existing empty file, just overwrite */
+ if (GNUNET_OK != make_empty_file (bf->fh, size * 4LL))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "write");
+ GNUNET_DISK_file_close (bf->fh);
+ GNUNET_free (bf);
+ return NULL;
+ }
+ }
+ else if (fsize != size * 4LL)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Size of file on disk is incorrect for this Bloom filter (want %llu, have %llu)\n"),
+ (unsigned long long) (size * 4LL),
+ (unsigned long long) fsize);
+ GNUNET_DISK_file_close (bf->fh);
+ GNUNET_free (bf);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* file did not exist, don't read, just create */
+ must_read = GNUNET_NO;
+ bf->fh =
+ GNUNET_DISK_file_open (filename,
+ GNUNET_DISK_OPEN_CREATE |
+ GNUNET_DISK_OPEN_READWRITE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ if (NULL == bf->fh)
+ {
+ GNUNET_free (bf);
+ return NULL;
+ }
+ if (GNUNET_OK != make_empty_file (bf->fh, size * 4LL))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
+ "write");
+ GNUNET_DISK_file_close (bf->fh);
+ GNUNET_free (bf);
+ return NULL;
+ }
+ }
+ bf->filename = GNUNET_strdup (filename);
+ /* Alloc block */
+ bf->bitArray = GNUNET_malloc_large (size);
+ if (bf->bitArray == NULL)
+ {
+ if (bf->fh != NULL)
+ GNUNET_DISK_file_close (bf->fh);
+ GNUNET_free (bf->filename);
+ GNUNET_free (bf);
+ return NULL;
+ }
+ bf->bitArraySize = size;
+ bf->addressesPerElement = k;
+ memset (bf->bitArray, 0, bf->bitArraySize);
+
+ if (GNUNET_YES != must_read)
+ return bf; /* already done! */
+ /* Read from the file what bits we can */
+ rbuff = GNUNET_malloc (BUFFSIZE);
+ pos = 0;
+ while (pos < size * 8LL)
+ {
+ int res;
+
+ res = GNUNET_DISK_file_read (bf->fh, rbuff, BUFFSIZE);
+ if (res == -1)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "read", bf->filename);
+ GNUNET_free (rbuff);
+ GNUNET_free (bf->filename);
+ GNUNET_DISK_file_close (bf->fh);
+ GNUNET_free (bf);
+ return NULL;
+ }
+ 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, size_t size,
+ unsigned int k)
+{
+ struct GNUNET_CONTAINER_BloomFilter *bf;
+ size_t 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->filename = NULL;
+ bf->fh = NULL;
+ bf->bitArray = GNUNET_malloc_large (size);
+ if (bf->bitArray == NULL)
+ {
+ GNUNET_free (bf);
+ return NULL;
+ }
+ 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 bf bloomfilter to take the raw data from
+ * @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 (const struct
+ GNUNET_CONTAINER_BloomFilter *bf,
+ char *data, size_t 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->fh != NULL)
+ GNUNET_DISK_file_close (bf->fh);
+ 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->filename != NULL)
+ make_empty_file (bf->fh, bf->bitArraySize * 4LL);
+}
+
+
+/**
+ * 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 (const 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, bf, 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
+ * @param data the data to or-in
+ * @param size number of bytes in data
+ */
+int
+GNUNET_CONTAINER_bloomfilter_or (struct GNUNET_CONTAINER_BloomFilter *bf,
+ const char *data, size_t size)
+{
+ unsigned int i;
+ unsigned int n;
+ unsigned long long *fc;
+ const unsigned long long *dc;
+
+ if (NULL == bf)
+ return GNUNET_YES;
+ if (bf->bitArraySize != size)
+ return GNUNET_SYSERR;
+ fc = (unsigned long long *) bf->bitArray;
+ dc = (const unsigned long long *) data;
+ n = size / sizeof (unsigned long long);
+
+ for (i = 0; i < n; i++)
+ fc[i] |= dc[i];
+ for (i = n * sizeof (unsigned long long); i < size; i++)
+ bf->bitArray[i] |= data[i];
+ return GNUNET_OK;
+}
+
+/**
+ * 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
+ * @param to_or the bloomfilter to or-in
+ * @param size number of bytes in data
+ */
+int
+GNUNET_CONTAINER_bloomfilter_or2 (struct GNUNET_CONTAINER_BloomFilter *bf,
+ const struct GNUNET_CONTAINER_BloomFilter
+ *to_or, size_t size)
+{
+ unsigned int i;
+ unsigned int n;
+ unsigned long long *fc;
+ const unsigned long long *dc;
+
+ if (NULL == bf)
+ return GNUNET_YES;
+ if (bf->bitArraySize != size)
+ return GNUNET_SYSERR;
+ fc = (unsigned long long *) bf->bitArray;
+ dc = (const unsigned long long *) to_or->bitArray;
+ n = size / sizeof (unsigned long long);
+
+ for (i = 0; i < n; i++)
+ fc[i] |= dc[i];
+ for (i = n * sizeof (unsigned long long); i < size; i++)
+ bf->bitArray[i] |= to_or->bitArray[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->filename == NULL)
+ return;
+ iterateBits (bf, &decrementBitCallback, bf, 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_cls 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_cls, size_t 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->filename != NULL)
+ make_empty_file (bf->fh, bf->bitArraySize * 4LL);
+ while (GNUNET_YES == iterator (iterator_cls, &hc))
+ 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 0000000..c34e220
--- /dev/null
+++ b/src/util/container_heap.c
@@ -0,0 +1,550 @@
+/*
+ This file is part of GNUnet.
+ (C) 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_heap.c
+ * @brief Implementation of a heap
+ * @author Nathan Evans
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define DEBUG 0
+
+/**
+ * Node in the heap.
+ */
+struct GNUNET_CONTAINER_HeapNode
+{
+ /**
+ * Heap this node belongs to.
+ */
+ struct GNUNET_CONTAINER_Heap *heap;
+
+ /**
+ * Parent node.
+ */
+ struct GNUNET_CONTAINER_HeapNode *parent;
+
+ /**
+ * Left child.
+ */
+ struct GNUNET_CONTAINER_HeapNode *left_child;
+
+ /**
+ * Right child.
+ */
+ struct GNUNET_CONTAINER_HeapNode *right_child;
+
+ /**
+ * Our element.
+ */
+ void *element;
+
+ /**
+ * Cost for this element.
+ */
+ GNUNET_CONTAINER_HeapCostType cost;
+
+ /**
+ * Number of elements below this node in the heap
+ * (excluding this node itself).
+ */
+ unsigned int tree_size;
+
+};
+
+/**
+ * Handle to a node in a heap.
+ */
+struct GNUNET_CONTAINER_Heap
+{
+
+ /**
+ * Root of the heap.
+ */
+ struct GNUNET_CONTAINER_HeapNode *root;
+
+ /**
+ * Current position of our random walk.
+ */
+ struct GNUNET_CONTAINER_HeapNode *walk_pos;
+
+ /**
+ * Number of elements in the heap.
+ */
+ unsigned int size;
+
+ /**
+ * How is the heap sorted?
+ */
+ enum GNUNET_CONTAINER_HeapOrder order;
+
+};
+
+
+#if DEBUG
+/**
+ * Check if internal invariants hold for the given node.
+ *
+ * @param node subtree to check
+ */
+static void
+check (const struct GNUNET_CONTAINER_HeapNode *node)
+{
+ if (NULL == node)
+ return;
+ GNUNET_assert (node->tree_size ==
+ ((node->left_child ==
+ NULL) ? 0 : 1 + node->left_child->tree_size) +
+ ((node->right_child ==
+ NULL) ? 0 : 1 + node->right_child->tree_size));
+ check (node->left_child);
+ check (node->right_child);
+}
+
+
+#define CHECK(n) check(n)
+#else
+#define CHECK(n) do {} while (0)
+#endif
+
+
+/**
+ * Create a new heap.
+ *
+ * @param order how should the heap be sorted?
+ * @return handle to the heap
+ */
+struct GNUNET_CONTAINER_Heap *
+GNUNET_CONTAINER_heap_create (enum GNUNET_CONTAINER_HeapOrder order)
+{
+ struct GNUNET_CONTAINER_Heap *heap;
+
+ heap = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_Heap));
+ heap->order = order;
+ return heap;
+}
+
+
+/**
+ * Destroys the heap. Only call on a heap that
+ * is already empty.
+ *
+ * @param heap heap to destroy
+ */
+void
+GNUNET_CONTAINER_heap_destroy (struct GNUNET_CONTAINER_Heap *heap)
+{
+ GNUNET_break (heap->size == 0);
+ GNUNET_free (heap);
+}
+
+
+/**
+ * Get element stored at root of heap.
+ *
+ * @param heap heap to inspect
+ * @return NULL if heap is empty
+ */
+void *
+GNUNET_CONTAINER_heap_peek (const struct GNUNET_CONTAINER_Heap *heap)
+{
+ if (heap->root == NULL)
+ return NULL;
+ return heap->root->element;
+}
+
+
+/**
+ * Get the current size of the heap
+ *
+ * @param heap the heap to get the size of
+ * @return number of elements stored
+ */
+unsigned int
+GNUNET_CONTAINER_heap_get_size (const struct GNUNET_CONTAINER_Heap *heap)
+{
+ return heap->size;
+}
+
+
+/**
+ * Get the current cost of the node
+ *
+ * @param node the node to get the cost of
+ * @return cost of the node
+ */
+GNUNET_CONTAINER_HeapCostType
+GNUNET_CONTAINER_heap_node_get_cost (const struct GNUNET_CONTAINER_HeapNode
+ *node)
+{
+ return node->cost;
+}
+
+/**
+ * Iterate over the children of the given node.
+ *
+ * @param heap argument to give to iterator
+ * @param node node to iterate over
+ * @param iterator function to call on each node
+ * @param iterator_cls closure for iterator
+ * @return GNUNET_YES to continue to iterate
+ */
+static int
+node_iterator (const struct GNUNET_CONTAINER_Heap *heap,
+ struct GNUNET_CONTAINER_HeapNode *node,
+ GNUNET_CONTAINER_HeapIterator iterator, void *iterator_cls)
+{
+ if (node == NULL)
+ return GNUNET_YES;
+ if (GNUNET_YES !=
+ node_iterator (heap, node->left_child, iterator, iterator_cls))
+ return GNUNET_NO;
+ if (GNUNET_YES !=
+ node_iterator (heap, node->right_child, iterator, iterator_cls))
+ return GNUNET_NO;
+ return iterator (iterator_cls, node, node->element, node->cost);
+}
+
+
+/**
+ * Iterate over all entries in the heap.
+ *
+ * @param heap the heap
+ * @param iterator function to call on each entry
+ * @param iterator_cls closure for iterator
+ */
+void
+GNUNET_CONTAINER_heap_iterate (const struct GNUNET_CONTAINER_Heap *heap,
+ GNUNET_CONTAINER_HeapIterator iterator,
+ void *iterator_cls)
+{
+ (void) node_iterator (heap, heap->root, iterator, iterator_cls);
+}
+
+
+/**
+ * Perform a random walk of the tree. The walk is biased
+ * towards elements closer to the root of the tree (since
+ * each walk starts at the root and ends at a random leaf).
+ * The heap internally tracks the current position of the
+ * walk.
+ *
+ * @param heap heap to walk
+ * @return data stored at the next random node in the walk;
+ * NULL if the tree is empty.
+ */
+void *
+GNUNET_CONTAINER_heap_walk_get_next (struct GNUNET_CONTAINER_Heap *heap)
+{
+ struct GNUNET_CONTAINER_HeapNode *pos;
+ void *element;
+
+ if (heap->root == NULL)
+ return NULL;
+ pos = heap->walk_pos;
+ if (pos == NULL)
+ pos = heap->root;
+ element = pos->element;
+ heap->walk_pos =
+ (0 ==
+ GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
+ 2)) ? pos->right_child : pos->left_child;
+ return element;
+}
+
+
+/**
+ * Insert the given node 'node' into the subtree starting
+ * at 'pos' (while keeping the tree somewhat balanced).
+ *
+ * @param heap heap to modify
+ * @param pos existing tree
+ * @param node node to insert (which may be a subtree itself)
+ */
+static void
+insert_node (struct GNUNET_CONTAINER_Heap *heap,
+ struct GNUNET_CONTAINER_HeapNode *pos,
+ struct GNUNET_CONTAINER_HeapNode *node)
+{
+ struct GNUNET_CONTAINER_HeapNode *parent;
+
+ GNUNET_assert (node->parent == NULL);
+ while ((heap->order == GNUNET_CONTAINER_HEAP_ORDER_MAX) ? (pos->cost >=
+ node->cost)
+ : (pos->cost <= node->cost))
+ {
+ /* node is descendent of pos */
+ pos->tree_size += (1 + node->tree_size);
+ if (pos->left_child == NULL)
+ {
+ pos->left_child = node;
+ node->parent = pos;
+ return;
+ }
+ if (pos->right_child == NULL)
+ {
+ pos->right_child = node;
+ node->parent = pos;
+ return;
+ }
+ /* keep it balanced by descending into smaller subtree */
+ if (pos->left_child->tree_size < pos->right_child->tree_size)
+ pos = pos->left_child;
+ else
+ pos = pos->right_child;
+ }
+ /* make 'node' parent of 'pos' */
+ parent = pos->parent;
+ pos->parent = NULL;
+ node->parent = parent;
+ if (NULL == parent)
+ {
+ heap->root = node;
+ }
+ else
+ {
+ if (parent->left_child == pos)
+ parent->left_child = node;
+ else
+ parent->right_child = node;
+ }
+ /* insert 'pos' below 'node' */
+ insert_node (heap, node, pos);
+ CHECK (pos);
+}
+
+
+/**
+ * Inserts a new element into the heap.
+ *
+ * @param heap heap to modify
+ * @param element element to insert
+ * @param cost cost for the element
+ * @return node for the new element
+ */
+struct GNUNET_CONTAINER_HeapNode *
+GNUNET_CONTAINER_heap_insert (struct GNUNET_CONTAINER_Heap *heap, void *element,
+ GNUNET_CONTAINER_HeapCostType cost)
+{
+ struct GNUNET_CONTAINER_HeapNode *node;
+
+ node = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_HeapNode));
+ node->heap = heap;
+ node->element = element;
+ node->cost = cost;
+ heap->size++;
+ if (NULL == heap->root)
+ heap->root = node;
+ else
+ insert_node (heap, heap->root, node);
+ GNUNET_assert (heap->size == heap->root->tree_size + 1);
+ CHECK (heap->root);
+ return node;
+}
+
+
+/**
+ * Remove root of the heap.
+ *
+ * @param heap heap to modify
+ * @return element data stored at the root node, NULL if heap is empty
+ */
+void *
+GNUNET_CONTAINER_heap_remove_root (struct GNUNET_CONTAINER_Heap *heap)
+{
+ void *ret;
+ struct GNUNET_CONTAINER_HeapNode *root;
+
+ if (NULL == (root = heap->root))
+ return NULL;
+ heap->size--;
+ ret = root->element;
+ if (root->left_child == NULL)
+ {
+ heap->root = root->right_child;
+ if (root->right_child != NULL)
+ root->right_child->parent = NULL;
+ }
+ else if (root->right_child == NULL)
+ {
+ heap->root = root->left_child;
+ root->left_child->parent = NULL;
+ }
+ else
+ {
+ root->left_child->parent = NULL;
+ root->right_child->parent = NULL;
+ heap->root = root->left_child;
+ insert_node (heap, heap->root, root->right_child);
+ }
+ GNUNET_free (root);
+#if DEBUG
+ GNUNET_assert (((heap->size == 0) && (heap->root == NULL)) ||
+ (heap->size == heap->root->tree_size + 1));
+ CHECK (heap->root);
+#endif
+ return ret;
+}
+
+
+/**
+ * Remove the given node 'node' from the tree and update
+ * the 'tree_size' fields accordingly. Preserves the
+ * children of 'node' and does NOT change the overall
+ * 'size' field of the tree.
+ */
+static void
+remove_node (struct GNUNET_CONTAINER_HeapNode *node)
+{
+ struct GNUNET_CONTAINER_HeapNode *ancestor;
+ struct GNUNET_CONTAINER_Heap *heap = node->heap;
+
+ /* update 'size' of the ancestors */
+ ancestor = node;
+ while (NULL != (ancestor = ancestor->parent))
+ ancestor->tree_size--;
+
+ /* update 'size' of node itself */
+ if (node->left_child != NULL)
+ node->tree_size -= (1 + node->left_child->tree_size);
+ if (node->right_child != NULL)
+ node->tree_size -= (1 + node->right_child->tree_size);
+
+ /* unlink 'node' itself and insert children in its place */
+ if (node->parent == NULL)
+ {
+ if (node->left_child != NULL)
+ {
+ heap->root = node->left_child;
+ node->left_child->parent = NULL;
+ if (node->right_child != NULL)
+ {
+ node->right_child->parent = NULL;
+ insert_node (heap, heap->root, node->right_child);
+ }
+ }
+ else
+ {
+ heap->root = node->right_child;
+ if (node->right_child != NULL)
+ node->right_child->parent = NULL;
+ }
+ }
+ else
+ {
+ if (node->parent->left_child == node)
+ node->parent->left_child = NULL;
+ else
+ node->parent->right_child = NULL;
+ if (node->left_child != NULL)
+ {
+ node->left_child->parent = NULL;
+ node->parent->tree_size -= (1 + node->left_child->tree_size);
+ insert_node (heap, node->parent, node->left_child);
+ }
+ if (node->right_child != NULL)
+ {
+ node->right_child->parent = NULL;
+ node->parent->tree_size -= (1 + node->right_child->tree_size);
+ insert_node (heap, node->parent, node->right_child);
+ }
+ }
+ node->parent = NULL;
+ node->left_child = NULL;
+ node->right_child = NULL;
+ GNUNET_assert (node->tree_size == 0);
+ CHECK (heap->root);
+}
+
+
+/**
+ * Removes a node from the heap.
+ *
+ * @param node node to remove
+ * @return element data stored at the node
+ */
+void *
+GNUNET_CONTAINER_heap_remove_node (struct GNUNET_CONTAINER_HeapNode *node)
+{
+ void *ret;
+ struct GNUNET_CONTAINER_Heap *heap;
+
+ heap = node->heap;
+ CHECK (heap->root);
+ if (heap->walk_pos == node)
+ (void) GNUNET_CONTAINER_heap_walk_get_next (heap);
+ remove_node (node);
+ heap->size--;
+ ret = node->element;
+ if (heap->walk_pos == node)
+ heap->walk_pos = NULL;
+ GNUNET_free (node);
+#if DEBUG
+ CHECK (heap->root);
+ GNUNET_assert (((heap->size == 0) && (heap->root == NULL)) ||
+ (heap->size == heap->root->tree_size + 1));
+#endif
+ return ret;
+}
+
+
+/**
+ * Updates the cost of any node in the tree
+ *
+ * @param heap heap to modify
+ * @param node node for which the cost is to be changed
+ * @param new_cost new cost for the node
+ */
+void
+GNUNET_CONTAINER_heap_update_cost (struct GNUNET_CONTAINER_Heap *heap,
+ struct GNUNET_CONTAINER_HeapNode *node,
+ GNUNET_CONTAINER_HeapCostType new_cost)
+{
+#if DEBUG
+ GNUNET_assert (((heap->size == 0) && (heap->root == NULL)) ||
+ (heap->size == heap->root->tree_size + 1));
+ CHECK (heap->root);
+#endif
+ remove_node (node);
+#if DEBUG
+ CHECK (heap->root);
+ GNUNET_assert (((heap->size == 1) && (heap->root == NULL)) ||
+ (heap->size == heap->root->tree_size + 2));
+#endif
+ node->cost = new_cost;
+ if (heap->root == NULL)
+ heap->root = node;
+ else
+ insert_node (heap, heap->root, node);
+#if DEBUG
+ CHECK (heap->root);
+ GNUNET_assert (((heap->size == 0) && (heap->root == NULL)) ||
+ (heap->size == heap->root->tree_size + 1));
+#endif
+}
+
+
+/* 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 0000000..b1051c8
--- /dev/null
+++ b/src/util/container_meta_data.c
@@ -0,0 +1,1180 @@
+/*
+ This file is part of GNUnet.
+ (C) 2003, 2004, 2005, 2006, 2008, 2009, 2010 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 <extractor.h>
+#include <zlib.h>
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Meta data item.
+ */
+struct MetaItem
+{
+ /**
+ * This is a linked list.
+ */
+ struct MetaItem *next;
+
+ /**
+ * Name of the extracting plugin.
+ */
+ char *plugin_name;
+
+ /**
+ * Mime-type of data.
+ */
+ char *mime_type;
+
+ /**
+ * The actual meta data.
+ */
+ char *data;
+
+ /**
+ * Number of bytes in 'data'.
+ */
+ size_t data_size;
+
+ /**
+ * Type of the meta data.
+ */
+ enum EXTRACTOR_MetaType type;
+
+ /**
+ * Format of the meta data.
+ */
+ enum EXTRACTOR_MetaFormat format;
+
+};
+
+/**
+ * Meta data to associate with a file, directory or namespace.
+ */
+struct GNUNET_CONTAINER_MetaData
+{
+ /**
+ * Linked list of the meta data items.
+ */
+ struct MetaItem *items;
+
+ /**
+ * Complete serialized and compressed buffer of the items.
+ * NULL if we have not computed that buffer yet.
+ */
+ char *sbuf;
+
+ /**
+ * Number of bytes in 'sbuf'. 0 if the buffer is stale.
+ */
+ size_t sbuf_size;
+
+ /**
+ * Number of items in the linked list.
+ */
+ unsigned int item_count;
+
+};
+
+
+/**
+ * Create a fresh struct CONTAINER_MetaData token.
+ *
+ * @return empty meta-data container
+ */
+struct GNUNET_CONTAINER_MetaData *
+GNUNET_CONTAINER_meta_data_create ()
+{
+ return GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MetaData));
+}
+
+
+/**
+ * Free meta data item.
+ *
+ * @param item item to free
+ */
+static void
+meta_item_free (struct MetaItem *item)
+{
+ GNUNET_free_non_null (item->plugin_name);
+ GNUNET_free_non_null (item->mime_type);
+ GNUNET_free_non_null (item->data);
+ GNUNET_free (item);
+}
+
+
+/**
+ * The meta data has changed, invalidate its serialization
+ * buffer.
+ *
+ * @param md meta data that changed
+ */
+static void
+invalidate_sbuf (struct GNUNET_CONTAINER_MetaData *md)
+{
+ if (md->sbuf == NULL)
+ return;
+ GNUNET_free (md->sbuf);
+ md->sbuf = NULL;
+ md->sbuf_size = 0;
+}
+
+
+/**
+ * Free meta data.
+ *
+ * @param md what to free
+ */
+void
+GNUNET_CONTAINER_meta_data_destroy (struct GNUNET_CONTAINER_MetaData *md)
+{
+ struct MetaItem *item;
+
+ if (md == NULL)
+ return;
+ while (NULL != (item = md->items))
+ {
+ md->items = item->next;
+ meta_item_free (item);
+ }
+ GNUNET_free_non_null (md->sbuf);
+ GNUNET_free (md);
+}
+
+
+/**
+ * Remove all items in the container.
+ *
+ * @param md metadata to manipulate
+ */
+void
+GNUNET_CONTAINER_meta_data_clear (struct GNUNET_CONTAINER_MetaData *md)
+{
+ struct MetaItem *item;
+
+ if (md == NULL)
+ return;
+ while (NULL != (item = md->items))
+ {
+ md->items = item->next;
+ meta_item_free (item);
+ }
+ GNUNET_free_non_null (md->sbuf);
+ memset (md, 0, sizeof (struct GNUNET_CONTAINER_MetaData));
+}
+
+
+
+/**
+ * Test if two MDs are equal. We consider them equal if
+ * the meta types, formats and content match (we do not
+ * include the mime types and plugins names in this
+ * consideration).
+ *
+ * @param md1 first value to check
+ * @param md2 other value to check
+ * @return GNUNET_YES if they are equal
+ */
+int
+GNUNET_CONTAINER_meta_data_test_equal (const struct GNUNET_CONTAINER_MetaData
+ *md1,
+ const struct GNUNET_CONTAINER_MetaData
+ *md2)
+{
+ struct MetaItem *i;
+ struct MetaItem *j;
+ int found;
+
+ if (md1 == md2)
+ return GNUNET_YES;
+ if (md1->item_count != md2->item_count)
+ return GNUNET_NO;
+
+ i = md1->items;
+ while (NULL != i)
+ {
+ found = GNUNET_NO;
+ j = md2->items;
+ while (NULL != j)
+ {
+ if ((i->type == j->type) && (i->format == j->format) &&
+ (i->data_size == j->data_size) &&
+ (0 == memcmp (i->data, j->data, i->data_size)))
+ {
+ found = GNUNET_YES;
+ break;
+ }
+ j = j->next;
+ }
+ if (found == GNUNET_NO)
+ return GNUNET_NO;
+ i = i->next;
+ }
+ return GNUNET_YES;
+}
+
+
+/**
+ * Extend metadata. Note that the list of meta data items is
+ * sorted by size (largest first).
+ *
+ * @param md metadata to extend
+ * @param plugin_name name of the plugin that produced this value;
+ * special values can be used (i.e. '&lt;zlib&gt;' for zlib being
+ * used in the main libextractor library and yielding
+ * meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data
+ * @param data_mime_type mime-type of data (not of the original file);
+ * can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return GNUNET_OK on success, GNUNET_SYSERR if this entry already exists
+ * data_mime_type and plugin_name are not considered for "exists" checks
+ */
+int
+GNUNET_CONTAINER_meta_data_insert (struct GNUNET_CONTAINER_MetaData *md,
+ const char *plugin_name,
+ enum EXTRACTOR_MetaType type,
+ enum EXTRACTOR_MetaFormat format,
+ const char *data_mime_type, const char *data,
+ size_t data_len)
+{
+ struct MetaItem *prev;
+ struct MetaItem *pos;
+ struct MetaItem *i;
+ char *p;
+
+ prev = NULL;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ if (pos->data_size < data_len)
+ break;
+ if ((pos->type == type) && (pos->data_size == data_len) &&
+ (0 == memcmp (pos->data, data, data_len)))
+ {
+ if ((pos->mime_type == NULL) && (data_mime_type != NULL))
+ {
+ pos->mime_type = GNUNET_strdup (data_mime_type);
+ invalidate_sbuf (md);
+ }
+ if ((pos->format == EXTRACTOR_METAFORMAT_C_STRING) &&
+ (format == EXTRACTOR_METAFORMAT_UTF8))
+ {
+ pos->format = EXTRACTOR_METAFORMAT_UTF8;
+ invalidate_sbuf (md);
+ }
+ return GNUNET_SYSERR;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+ md->item_count++;
+ i = GNUNET_malloc (sizeof (struct MetaItem));
+ i->type = type;
+ i->format = format;
+ i->data_size = data_len;
+ i->next = pos;
+ if (prev == NULL)
+ md->items = i;
+ else
+ prev->next = i;
+ i->mime_type =
+ (data_mime_type == NULL) ? NULL : GNUNET_strdup (data_mime_type);
+ i->plugin_name = (plugin_name == NULL) ? NULL : GNUNET_strdup (plugin_name);
+ i->data = GNUNET_malloc (data_len);
+ memcpy (i->data, data, data_len);
+ /* change OS native dir separators to unix '/' and others to '_' */
+ if ( (type == EXTRACTOR_METATYPE_FILENAME) ||
+ (type == EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME) )
+ {
+ p = i->data;
+ while ((*p != '\0') && (p < i->data + data_len))
+ {
+ if (*p == DIR_SEPARATOR)
+ *p = '/';
+ else if (*p == '\\')
+ *p = '_';
+ p++;
+ }
+ }
+ invalidate_sbuf (md);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Merge given meta data.
+ *
+ * @param cls the 'struct GNUNET_CONTAINER_MetaData' to merge into
+ * @param plugin_name name of the plugin that produced this value;
+ * special values can be used (i.e. '&lt;zlib&gt;' for zlib being
+ * used in the main libextractor library and yielding
+ * meta data).
+ * @param type libextractor-type describing the meta data
+ * @param format basic format information about data
+ * @param data_mime_type mime-type of data (not of the original file);
+ * can be NULL (if mime-type is not known)
+ * @param data actual meta-data found
+ * @param data_len number of bytes in data
+ * @return 0 (to continue)
+ */
+static int
+merge_helper (void *cls, const char *plugin_name, enum EXTRACTOR_MetaType type,
+ enum EXTRACTOR_MetaFormat format, const char *data_mime_type,
+ const char *data, size_t data_len)
+{
+ struct GNUNET_CONTAINER_MetaData *md = cls;
+
+ (void) GNUNET_CONTAINER_meta_data_insert (md, plugin_name, type, format,
+ data_mime_type, data, data_len);
+ return 0;
+}
+
+
+/**
+ * Extend metadata. Merges the meta data from the second argument
+ * into the first, discarding duplicate key-value pairs.
+ *
+ * @param md metadata to extend
+ * @param in metadata to merge
+ */
+void
+GNUNET_CONTAINER_meta_data_merge (struct GNUNET_CONTAINER_MetaData *md,
+ const struct GNUNET_CONTAINER_MetaData *in)
+{
+ GNUNET_CONTAINER_meta_data_iterate (in, &merge_helper, md);
+}
+
+
+/**
+ * Remove an item.
+ *
+ * @param md metadata to manipulate
+ * @param type type of the item to remove
+ * @param data specific value to remove, NULL to remove all
+ * entries of the given type
+ * @param data_len number of bytes in data
+ * @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,
+ enum EXTRACTOR_MetaType type,
+ const char *data, size_t data_len)
+{
+ struct MetaItem *pos;
+ struct MetaItem *prev;
+
+ prev = NULL;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ if ((pos->type == type) &&
+ ((data == NULL) ||
+ ((pos->data_size == data_len) &&
+ (0 == memcmp (pos->data, data, data_len)))))
+ {
+ if (prev == NULL)
+ md->items = pos->next;
+ else
+ prev->next = pos->next;
+ meta_item_free (pos);
+ md->item_count--;
+ invalidate_sbuf (md);
+ return GNUNET_OK;
+ }
+ prev = pos;
+ pos = pos->next;
+ }
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Add the current time as the publication date
+ * to the meta-data.
+ *
+ * @param md metadata to modify
+ */
+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_METATYPE_PUBLICATION_DATE,
+ NULL, 0);
+ dat = GNUNET_STRINGS_absolute_time_to_string (t);
+ GNUNET_CONTAINER_meta_data_insert (md, "<gnunet>",
+ EXTRACTOR_METATYPE_PUBLICATION_DATE,
+ EXTRACTOR_METAFORMAT_UTF8, "text/plain",
+ dat, strlen (dat) + 1);
+ GNUNET_free (dat);
+}
+
+
+/**
+ * Iterate over MD entries.
+ *
+ * @param md metadata to inspect
+ * @param iter function to call on each entry
+ * @param iter_cls closure for iterator
+ * @return number of entries
+ */
+int
+GNUNET_CONTAINER_meta_data_iterate (const struct GNUNET_CONTAINER_MetaData *md,
+ EXTRACTOR_MetaDataProcessor iter,
+ void *iter_cls)
+{
+ struct MetaItem *pos;
+
+ if (md == NULL)
+ return 0;
+ if (iter == NULL)
+ return md->item_count;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ if (0 !=
+ iter (iter_cls, pos->plugin_name, pos->type, pos->format,
+ pos->mime_type, pos->data, pos->data_size))
+ return md->item_count;
+ pos = pos->next;
+ }
+ return md->item_count;
+}
+
+
+/**
+ * Get the first MD entry of the given type. Caller
+ * is responsible for freeing the return value.
+ * Also, only meta data items that are strings (0-terminated)
+ * are returned by this function.
+ *
+ * @param md metadata to inspect
+ * @param type type to look for
+ * @return NULL if no entry was found
+ */
+char *
+GNUNET_CONTAINER_meta_data_get_by_type (const struct GNUNET_CONTAINER_MetaData
+ *md, enum EXTRACTOR_MetaType type)
+{
+ struct MetaItem *pos;
+
+ if (md == NULL)
+ return NULL;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ if ((type == pos->type) &&
+ ((pos->format == EXTRACTOR_METAFORMAT_UTF8) ||
+ (pos->format == EXTRACTOR_METAFORMAT_C_STRING)))
+ return GNUNET_strdup (pos->data);
+ pos = pos->next;
+ }
+ return NULL;
+}
+
+
+/**
+ * Get the first matching MD entry of the given types. Caller is
+ * responsible for freeing the return value. Also, only meta data
+ * items that are strings (0-terminated) are returned by this
+ * function.
+ *
+ * @param md metadata to inspect
+ * @param ... -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,
+ ...)
+{
+ char *ret;
+ va_list args;
+ enum EXTRACTOR_MetaType type;
+
+ if (md == NULL)
+ return NULL;
+ ret = NULL;
+ va_start (args, md);
+ while (1)
+ {
+ type = va_arg (args, enum EXTRACTOR_MetaType);
+
+ 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 md metadata to get the thumbnail from
+ * @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)
+{
+ struct MetaItem *pos;
+ struct MetaItem *match;
+
+ if (md == NULL)
+ return 0;
+ match = NULL;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ if ((NULL != pos->mime_type) &&
+ (0 == strncasecmp ("image/", pos->mime_type, strlen ("image/"))) &&
+ (pos->format == EXTRACTOR_METAFORMAT_BINARY))
+ {
+ if (match == NULL)
+ match = pos;
+ else if ((match->type != EXTRACTOR_METATYPE_THUMBNAIL) &&
+ (pos->type == EXTRACTOR_METATYPE_THUMBNAIL))
+ match = pos;
+ }
+ pos = pos->next;
+ }
+ if ((match == NULL) || (match->data_size == 0))
+ return 0;
+ *thumb = GNUNET_malloc (match->data_size);
+ memcpy (*thumb, match->data, match->data_size);
+ return match->data_size;
+}
+
+
+/**
+ * Duplicate struct GNUNET_CONTAINER_MetaData.
+ *
+ * @param md what to duplicate
+ * @return duplicate meta-data container
+ */
+struct GNUNET_CONTAINER_MetaData *
+GNUNET_CONTAINER_meta_data_duplicate (const struct GNUNET_CONTAINER_MetaData
+ *md)
+{
+ struct GNUNET_CONTAINER_MetaData *ret;
+ struct MetaItem *pos;
+
+ if (md == NULL)
+ return NULL;
+ ret = GNUNET_CONTAINER_meta_data_create ();
+ pos = md->items;
+ while (NULL != pos)
+ {
+ GNUNET_CONTAINER_meta_data_insert (ret, pos->plugin_name, pos->type,
+ pos->format, pos->mime_type, pos->data,
+ pos->data_size);
+ pos = pos->next;
+ }
+ return ret;
+}
+
+
+
+/**
+ * Try to compress the given block of data.
+ *
+ * @param data block to compress; if compression
+ * resulted in a smaller block, the first
+ * bytes of data are updated to the compressed
+ * data
+ * @param oldSize number of bytes in data
+ * @param result set to the compressed data
+ * @param newSize set to size of result
+ * @return GNUNET_YES if compression reduce the size,
+ * GNUNET_NO if compression did not help
+ */
+static int
+try_compression (const char *data, size_t oldSize, char **result,
+ size_t * newSize)
+{
+ 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)
+ {
+ *result = tmp;
+ *newSize = dlen;
+ return GNUNET_YES;
+ }
+ }
+ GNUNET_free (tmp);
+ return GNUNET_NO;
+}
+
+
+/**
+ * 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
+
+
+/**
+ * Header for serialized meta data.
+ */
+struct MetaDataHeader
+{
+ /**
+ * The version of the MD serialization. The highest bit is used to
+ * indicate compression.
+ *
+ * Version 0 is traditional (pre-0.9) meta data (unsupported)
+ * Version is 1 for a NULL pointer
+ * Version 2 is for 0.9.x (and possibly higher)
+ * Other version numbers are not yet defined.
+ */
+ uint32_t version;
+
+ /**
+ * How many MD entries are there?
+ */
+ uint32_t entries;
+
+ /**
+ * Size of the decompressed meta data.
+ */
+ uint32_t size;
+
+ /**
+ * This is followed by 'entries' values of type 'struct MetaDataEntry'
+ * and then by 'entry' plugin names, mime-types and data blocks
+ * as specified in those meta data entries.
+ */
+};
+
+
+/**
+ * Entry of serialized meta data.
+ */
+struct MetaDataEntry
+{
+ /**
+ * Meta data type. Corresponds to an 'enum EXTRACTOR_MetaType'
+ */
+ uint32_t type;
+
+ /**
+ * Meta data format. Corresponds to an 'enum EXTRACTOR_MetaFormat'
+ */
+ uint32_t format;
+
+ /**
+ * Number of bytes of meta data.
+ */
+ uint32_t data_size;
+
+ /**
+ * Number of bytes in the plugin name including 0-terminator. 0 for NULL.
+ */
+ uint32_t plugin_name_len;
+
+ /**
+ * Number of bytes in the mime type including 0-terminator. 0 for NULL.
+ */
+ uint32_t mime_type_len;
+
+};
+
+
+/**
+ * Serialize meta-data to target.
+ *
+ * @param md metadata to serialize
+ * @param target where to write the serialized metadata;
+ * *target can be NULL, in which case memory is allocated
+ * @param max maximum number of bytes available in target
+ * @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)
+ */
+ssize_t
+GNUNET_CONTAINER_meta_data_serialize (const struct GNUNET_CONTAINER_MetaData
+ *md, char **target, size_t max,
+ enum
+ GNUNET_CONTAINER_MetaDataSerializationOptions
+ opt)
+{
+ struct GNUNET_CONTAINER_MetaData *vmd;
+ struct MetaItem *pos;
+ struct MetaDataHeader ihdr;
+ struct MetaDataHeader *hdr;
+ struct MetaDataEntry *ent;
+ char *dst;
+ unsigned int i;
+ uint64_t msize;
+ size_t off;
+ char *mdata;
+ char *cdata;
+ size_t mlen;
+ size_t plen;
+ size_t size;
+ size_t left;
+ size_t clen;
+ size_t rlen;
+ int comp;
+
+ if (max < sizeof (struct MetaDataHeader))
+ return GNUNET_SYSERR; /* far too small */
+ if (md == NULL)
+ return 0;
+
+ if (md->sbuf != NULL)
+ {
+ /* try to use serialization cache */
+ if (md->sbuf_size <= max)
+ {
+ if (NULL == *target)
+ *target = GNUNET_malloc (md->sbuf_size);
+ memcpy (*target, md->sbuf, md->sbuf_size);
+ return md->sbuf_size;
+ }
+ if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
+ return GNUNET_SYSERR; /* can say that this will fail */
+ /* need to compute a partial serialization, sbuf useless ... */
+ }
+ dst = NULL;
+ msize = 0;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ msize += sizeof (struct MetaDataEntry);
+ msize += pos->data_size;
+ if (pos->plugin_name != NULL)
+ msize += strlen (pos->plugin_name) + 1;
+ if (pos->mime_type != NULL)
+ msize += strlen (pos->mime_type) + 1;
+ pos = pos->next;
+ }
+ size = (size_t) msize;
+ if (size != msize)
+ {
+ GNUNET_break (0); /* integer overflow */
+ return GNUNET_SYSERR;
+ }
+ if (size >= GNUNET_MAX_MALLOC_CHECKED)
+ {
+ /* too large to be processed */
+ return GNUNET_SYSERR;
+ }
+ ent = GNUNET_malloc (size);
+ mdata = (char *) &ent[md->item_count];
+ off = size - (md->item_count * sizeof (struct MetaDataEntry));
+ i = 0;
+ pos = md->items;
+ while (NULL != pos)
+ {
+ ent[i].type = htonl ((uint32_t) pos->type);
+ ent[i].format = htonl ((uint32_t) pos->format);
+ ent[i].data_size = htonl ((uint32_t) pos->data_size);
+ if (pos->plugin_name == NULL)
+ plen = 0;
+ else
+ plen = strlen (pos->plugin_name) + 1;
+ ent[i].plugin_name_len = htonl ((uint32_t) plen);
+ if (pos->mime_type == NULL)
+ mlen = 0;
+ else
+ mlen = strlen (pos->mime_type) + 1;
+ ent[i].mime_type_len = htonl ((uint32_t) mlen);
+ off -= pos->data_size;
+ memcpy (&mdata[off], pos->data, pos->data_size);
+ off -= plen;
+ if (pos->plugin_name != NULL)
+ memcpy (&mdata[off], pos->plugin_name, plen);
+ off -= mlen;
+ if (pos->mime_type != NULL)
+ memcpy (&mdata[off], pos->mime_type, mlen);
+ i++;
+ pos = pos->next;
+ }
+ GNUNET_assert (off == 0);
+
+ clen = 0;
+ cdata = NULL;
+ left = size;
+ i = 0;
+ pos = md->items;
+ while (pos != NULL)
+ {
+ comp = GNUNET_NO;
+ if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_NO_COMPRESS))
+ comp = try_compression ((const char *) &ent[i], left, &cdata, &clen);
+
+ if ((md->sbuf == NULL) && (i == 0))
+ {
+ /* fill 'sbuf'; this "modifies" md, but since this is only
+ * an internal cache we will cast away the 'const' instead
+ * of making the API look strange. */
+ vmd = (struct GNUNET_CONTAINER_MetaData *) md;
+ hdr = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
+ hdr->size = htonl (left);
+ hdr->entries = htonl (md->item_count);
+ if (GNUNET_YES == comp)
+ {
+ GNUNET_assert (clen < left);
+ hdr->version = htonl (2 | HEADER_COMPRESSED);
+ memcpy (&hdr[1], cdata, clen);
+ vmd->sbuf_size = clen + sizeof (struct MetaDataHeader);
+ }
+ else
+ {
+ hdr->version = htonl (2);
+ memcpy (&hdr[1], &ent[0], left);
+ vmd->sbuf_size = left + sizeof (struct MetaDataHeader);
+ }
+ vmd->sbuf = (char *) hdr;
+ }
+
+ if (((left + sizeof (struct MetaDataHeader)) <= max) ||
+ ((comp == GNUNET_YES) && (clen <= max)))
+ {
+ /* success, this now fits! */
+ if (GNUNET_YES == comp)
+ {
+ if (dst == NULL)
+ dst = GNUNET_malloc (clen + sizeof (struct MetaDataHeader));
+ hdr = (struct MetaDataHeader *) dst;
+ hdr->version = htonl (2 | HEADER_COMPRESSED);
+ hdr->size = htonl (left);
+ hdr->entries = htonl (md->item_count - i);
+ memcpy (&dst[sizeof (struct MetaDataHeader)], cdata, clen);
+ GNUNET_free (cdata);
+ GNUNET_free (ent);
+ rlen = clen + sizeof (struct MetaDataHeader);
+ }
+ else
+ {
+ if (dst == NULL)
+ dst = GNUNET_malloc (left + sizeof (struct MetaDataHeader));
+ hdr = (struct MetaDataHeader *) dst;
+ hdr->version = htonl (2);
+ hdr->entries = htonl (md->item_count - i);
+ hdr->size = htonl (left);
+ memcpy (&dst[sizeof (struct MetaDataHeader)], &ent[i], left);
+ GNUNET_free (ent);
+ rlen = left + sizeof (struct MetaDataHeader);
+ }
+ if (NULL != *target)
+ {
+ memcpy (*target, dst, clen + sizeof (struct MetaDataHeader));
+ GNUNET_free (dst);
+ }
+ else
+ {
+ *target = dst;
+ }
+ return rlen;
+ }
+
+ if (0 == (opt & GNUNET_CONTAINER_META_DATA_SERIALIZE_PART))
+ {
+ /* does not fit! */
+ GNUNET_free (ent);
+ return GNUNET_SYSERR;
+ }
+
+ /* next iteration: ignore the corresponding meta data at the
+ * end and try again without it */
+ left -= sizeof (struct MetaDataEntry);
+ left -= pos->data_size;
+ if (pos->plugin_name != NULL)
+ left -= strlen (pos->plugin_name) + 1;
+ if (pos->mime_type != NULL)
+ left -= strlen (pos->mime_type) + 1;
+ pos = pos->next;
+ i++;
+ }
+ GNUNET_free (ent);
+
+ /* nothing fit, only write header! */
+ ihdr.version = htonl (2);
+ ihdr.entries = htonl (0);
+ ihdr.size = htonl (0);
+ if (*target == NULL)
+ *target = GNUNET_malloc (sizeof (struct MetaDataHeader));
+ memcpy (*target, &ihdr, sizeof (struct MetaDataHeader));
+ return sizeof (struct MetaDataHeader);
+}
+
+
+/**
+ * Get the size of the full meta-data in serialized form.
+ *
+ * @param md metadata to inspect
+ * @return number of bytes needed for serialization, -1 on error
+ */
+ssize_t
+GNUNET_CONTAINER_meta_data_get_serialized_size (const struct
+ GNUNET_CONTAINER_MetaData *md)
+{
+ ssize_t ret;
+ char *ptr;
+
+ if (md->sbuf != NULL)
+ return md->sbuf_size;
+ ptr = NULL;
+ ret =
+ GNUNET_CONTAINER_meta_data_serialize (md, &ptr, GNUNET_MAX_MALLOC_CHECKED,
+ GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
+ if (ret != -1)
+ GNUNET_free (ptr);
+ return ret;
+}
+
+
+/**
+ * Decompress input, return the decompressed data
+ * as output, set outputSize to the number of bytes
+ * that were found.
+ *
+ * @param input compressed data
+ * @param inputSize number of bytes in input
+ * @param outputSize expected size of the output
+ * @return NULL on error
+ */
+static char *
+decompress (const char *input, size_t inputSize, size_t 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;
+ }
+}
+
+
+/**
+ * Deserialize meta-data. Initializes md.
+ *
+ * @param input buffer with the serialized metadata
+ * @param size number of bytes available in input
+ * @return MD on success, NULL on error (i.e.
+ * bad format)
+ */
+struct GNUNET_CONTAINER_MetaData *
+GNUNET_CONTAINER_meta_data_deserialize (const char *input, size_t size)
+{
+ struct GNUNET_CONTAINER_MetaData *md;
+ struct MetaDataHeader hdr;
+ struct MetaDataEntry ent;
+ uint32_t ic;
+ uint32_t i;
+ char *data;
+ const char *cdata;
+ uint32_t version;
+ uint32_t dataSize;
+ int compressed;
+ size_t left;
+ uint32_t mlen;
+ uint32_t plen;
+ uint32_t dlen;
+ const char *mdata;
+ const char *meta_data;
+ const char *plugin_name;
+ const char *mime_type;
+ enum EXTRACTOR_MetaFormat format;
+
+ if (size < sizeof (struct MetaDataHeader))
+ return NULL;
+ memcpy (&hdr, input, sizeof (struct MetaDataHeader));
+ version = ntohl (hdr.version) & HEADER_VERSION_MASK;
+ compressed = (ntohl (hdr.version) & HEADER_COMPRESSED) != 0;
+
+ if (version == 1)
+ return NULL; /* null pointer */
+ if (version != 2)
+ {
+ GNUNET_break_op (0); /* unsupported version */
+ return NULL;
+ }
+
+ ic = ntohl (hdr.entries);
+ dataSize = ntohl (hdr.size);
+ if ((sizeof (struct MetaDataEntry) * ic) > dataSize)
+ {
+ GNUNET_break_op (0);
+ return NULL;
+ }
+
+ if (compressed)
+ {
+ if (dataSize >= GNUNET_MAX_MALLOC_CHECKED)
+ {
+ /* make sure we don't blow our memory limit because of a mal-formed
+ * message... */
+ GNUNET_break_op (0);
+ return NULL;
+ }
+ 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 *) &input[sizeof (struct MetaDataHeader)];
+ if (dataSize != size - sizeof (struct MetaDataHeader))
+ {
+ GNUNET_break_op (0);
+ return NULL;
+ }
+ }
+
+ md = GNUNET_CONTAINER_meta_data_create ();
+ left = dataSize - ic * sizeof (struct MetaDataEntry);
+ mdata = &cdata[ic * sizeof (struct MetaDataEntry)];
+ for (i = 0; i < ic; i++)
+ {
+ memcpy (&ent, &cdata[i * sizeof (struct MetaDataEntry)],
+ sizeof (struct MetaDataEntry));
+ format = (enum EXTRACTOR_MetaFormat) ntohl (ent.format);
+ if ((format != EXTRACTOR_METAFORMAT_UTF8) &&
+ (format != EXTRACTOR_METAFORMAT_C_STRING) &&
+ (format != EXTRACTOR_METAFORMAT_BINARY))
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ dlen = ntohl (ent.data_size);
+ plen = ntohl (ent.plugin_name_len);
+ mlen = ntohl (ent.mime_type_len);
+ if (dlen > left)
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ left -= dlen;
+ meta_data = &mdata[left];
+ if ((format == EXTRACTOR_METAFORMAT_UTF8) ||
+ (format == EXTRACTOR_METAFORMAT_C_STRING))
+ {
+ if ((dlen == 0) || (mdata[left + dlen - 1] != '\0'))
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ }
+ if (plen > left)
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ left -= plen;
+ if ((plen > 0) && (mdata[left + plen - 1] != '\0'))
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ if (plen == 0)
+ plugin_name = NULL;
+ else
+ plugin_name = &mdata[left];
+
+ if (mlen > left)
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ left -= mlen;
+ if ((mlen > 0) && (mdata[left + mlen - 1] != '\0'))
+ {
+ GNUNET_break_op (0);
+ break;
+ }
+ if (mlen == 0)
+ mime_type = NULL;
+ else
+ mime_type = &mdata[left];
+ GNUNET_CONTAINER_meta_data_insert (md, plugin_name,
+ (enum EXTRACTOR_MetaType)
+ ntohl (ent.type), format, mime_type,
+ meta_data, dlen);
+ }
+ GNUNET_free_non_null (data);
+ return md;
+}
+
+
+/* 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 0000000..7e53a64
--- /dev/null
+++ b/src/util/container_multihashmap.c
@@ -0,0 +1,493 @@
+/*
+ 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 may be present multiple times
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_crypto_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * An entry in the hash map.
+ */
+struct MapEntry
+{
+
+ /**
+ * Key for the entry.
+ */
+ GNUNET_HashCode key;
+
+ /**
+ * Value of the entry.
+ */
+ void *value;
+
+ /**
+ * If there is a hash collision, we create a linked list.
+ */
+ struct MapEntry *next;
+
+};
+
+/**
+ * Internal representation of the hash map.
+ */
+struct GNUNET_CONTAINER_MultiHashMap
+{
+
+ /**
+ * All of our buckets.
+ */
+ struct MapEntry **map;
+
+ /**
+ * Number of entries in the map.
+ */
+ unsigned int size;
+
+ /**
+ * Length of the "map" array.
+ */
+ unsigned int map_length;
+};
+
+
+/**
+ * Create a multi hash 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)
+{
+ struct GNUNET_CONTAINER_MultiHashMap *ret;
+
+ GNUNET_assert (len > 0);
+ ret = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_MultiHashMap));
+ ret->map = GNUNET_malloc (len * sizeof (struct MapEntry *));
+ ret->map_length = len;
+ return ret;
+}
+
+
+/**
+ * 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)
+{
+ 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);
+}
+
+
+/**
+ * Compute the index of the bucket for the given key.
+ *
+ * @param m hash map for which to compute the index
+ * @param key what key should the index be computed for
+ * @return offset into the "map" array of "m"
+ */
+static unsigned int
+idx_of (const struct GNUNET_CONTAINER_MultiHashMap *m,
+ const GNUNET_HashCode * key)
+{
+ GNUNET_assert (m != NULL);
+ return (*(unsigned int *) key) % m->map_length;
+}
+
+
+/**
+ * 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)
+{
+ return map->size;
+}
+
+
+/**
+ * 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)
+{
+ 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;
+}
+
+
+/**
+ * Iterate over all entries in the map.
+ *
+ * @param map the map
+ * @param it function to call on each entry
+ * @param it_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 it,
+ void *it_cls)
+{
+ int count;
+ unsigned int i;
+ struct MapEntry *e;
+ struct MapEntry *n;
+ GNUNET_HashCode kc;
+
+ count = 0;
+ GNUNET_assert (map != NULL);
+ for (i = 0; i < map->map_length; i++)
+ {
+ n = map->map[i];
+ while (NULL != (e = n))
+ {
+ n = e->next;
+ if (NULL != it)
+ {
+ kc = e->key;
+ if (GNUNET_OK != it (it_cls, &kc, e->value))
+ return GNUNET_SYSERR;
+ }
+ count++;
+ }
+ }
+ return count;
+}
+
+
+/**
+ * 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)
+{
+ 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;
+}
+
+
+/**
+ * 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)
+{
+ 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;
+}
+
+
+/**
+ * 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)
+{
+ struct MapEntry *e;
+
+ e = map->map[idx_of (map, key)];
+ while (e != NULL)
+ {
+ if (0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
+ return GNUNET_YES;
+ e = e->next;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Check if the map contains the given value under the given
+ * key.
+ *
+ * @param map the map
+ * @param key the key to test if a value exists for it
+ * @param value value to test for
+ * @return GNUNET_YES if such a value exists,
+ * GNUNET_NO if not
+ */
+int
+GNUNET_CONTAINER_multihashmap_contains_value (const struct
+ GNUNET_CONTAINER_MultiHashMap
+ *map, const GNUNET_HashCode * key,
+ const void *value)
+{
+ struct MapEntry *e;
+
+ e = map->map[idx_of (map, key)];
+ while (e != NULL)
+ {
+ if ((0 == memcmp (key, &e->key, sizeof (GNUNET_HashCode))) &&
+ (e->value == value))
+ return GNUNET_YES;
+ e = e->next;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Grow the given map to a more appropriate size.
+ *
+ * @param map the hash map to grow
+ */
+static void
+grow (struct GNUNET_CONTAINER_MultiHashMap *map)
+{
+ struct MapEntry **old_map;
+ struct MapEntry **new_map;
+ struct MapEntry *e;
+ unsigned int old_len;
+ unsigned int new_len;
+ unsigned int idx;
+ unsigned int i;
+
+ old_map = map->map;
+ old_len = map->map_length;
+ new_len = old_len * 2;
+ new_map = GNUNET_malloc (sizeof (struct MapEntry *) * new_len);
+ map->map_length = new_len;
+ map->map = new_map;
+ for (i = 0; i < old_len; i++)
+ {
+ while (NULL != (e = old_map[i]))
+ {
+ old_map[i] = e->next;
+ idx = idx_of (map, &e->key);
+ e->next = new_map[idx];
+ new_map[idx] = e;
+ }
+ }
+ GNUNET_free (old_map);
+}
+
+
+/**
+ * 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)
+{
+ 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)))
+ {
+ 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);
+ i = idx_of (map, key);
+ }
+ 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;
+}
+
+
+/**
+ * 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 it function to call on each entry
+ * @param it_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 it,
+ void *it_cls)
+{
+ int count;
+ struct MapEntry *e;
+ struct MapEntry *n;
+
+ count = 0;
+ n = map->map[idx_of (map, key)];
+ while (NULL != (e = n))
+ {
+ n = e->next;
+ if (0 != memcmp (key, &e->key, sizeof (GNUNET_HashCode)))
+ continue;
+ if ((it != NULL) && (GNUNET_OK != it (it_cls, key, e->value)))
+ return GNUNET_SYSERR;
+ count++;
+ }
+ return count;
+}
+
+
+/* end of container_multihashmap.c */
diff --git a/src/util/container_slist.c b/src/util/container_slist.c
new file mode 100644
index 0000000..7b85dc8
--- /dev/null
+++ b/src/util/container_slist.c
@@ -0,0 +1,387 @@
+/*
+ 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/container_slist.c
+ * @brief Implementation of a singly-linked list
+ * @author Nils Durner
+ */
+
+#include "platform.h"
+#include "gnunet_container_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Element in our linked list.
+ */
+struct GNUNET_CONTAINER_SList_Elem
+{
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_CONTAINER_SList_Elem *next;
+
+ /**
+ * Application data stored at this element.
+ */
+ void *elem;
+
+ /**
+ * Number of bytes stored in elem.
+ */
+ size_t len;
+
+ /**
+ * Disposition of the element.
+ */
+ enum GNUNET_CONTAINER_SListDisposition disp;
+};
+
+
+/**
+ * Handle to a singly linked list
+ */
+struct GNUNET_CONTAINER_SList
+{
+ /**
+ * Head of the linked list.
+ */
+ struct GNUNET_CONTAINER_SList_Elem *head;
+
+ /**
+ * Tail of the linked list.
+ */
+ struct GNUNET_CONTAINER_SList_Elem *tail;
+
+ /**
+ * Number of elements in the list.
+ */
+ unsigned int length;
+};
+
+
+
+/**
+ * Create a new element that is to be inserted into the list
+ * @internal
+ * @param disp memory disposition
+ * @param buf payload buffer
+ * @param len length of the buffer
+ * @return a new element
+ */
+static struct GNUNET_CONTAINER_SList_Elem *
+create_elem (enum GNUNET_CONTAINER_SListDisposition disp, const void *buf,
+ size_t len)
+{
+ struct GNUNET_CONTAINER_SList_Elem *e;
+
+ if (disp == GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT)
+ {
+ e = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_SList_Elem) + len);
+ memcpy (&e[1], buf, len);
+ e->elem = (void *) &e[1];
+ }
+ else
+ {
+ e = GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_SList_Elem));
+ e->elem = (void *) buf;
+ }
+ e->disp = disp;
+ e->len = len;
+ return e;
+}
+
+
+/**
+ * Add a new element to the list
+ * @param l list
+ * @param disp memory disposition
+ * @param buf payload buffer
+ * @param len length of the buffer
+ */
+void
+GNUNET_CONTAINER_slist_add (struct GNUNET_CONTAINER_SList *l,
+ enum GNUNET_CONTAINER_SListDisposition disp,
+ const void *buf, size_t len)
+{
+ struct GNUNET_CONTAINER_SList_Elem *e;
+
+ e = create_elem (disp, buf, len);
+ e->next = l->head;
+ l->head = e;
+ if (l->tail == NULL)
+ l->tail = e;
+ l->length++;
+}
+
+/**
+ * Add a new element to the end of the list
+ * @param l list
+ * @param disp memory disposition
+ * @param buf payload buffer
+ * @param len length of the buffer
+ */
+void
+GNUNET_CONTAINER_slist_add_end (struct GNUNET_CONTAINER_SList *l,
+ enum GNUNET_CONTAINER_SListDisposition disp,
+ const void *buf, size_t len)
+{
+ struct GNUNET_CONTAINER_SList_Elem *e;
+
+ e = create_elem (disp, buf, len);
+ if (l->tail != NULL)
+ l->tail->next = e;
+ if (l->head == NULL)
+ l->head = e;
+ l->tail = e;
+ l->length++;
+}
+
+
+/**
+ * Append a singly linked list to another
+ * @param dst list to append to
+ * @param src source
+ */
+void
+GNUNET_CONTAINER_slist_append (struct GNUNET_CONTAINER_SList *dst,
+ struct GNUNET_CONTAINER_SList *src)
+{
+ struct GNUNET_CONTAINER_SList_Iterator i;
+
+ for (i = GNUNET_CONTAINER_slist_begin (src);
+ GNUNET_CONTAINER_slist_end (&i) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&i))
+
+ {
+ GNUNET_CONTAINER_slist_add (dst,
+ (i.elem->disp ==
+ GNUNET_CONTAINER_SLIST_DISPOSITION_STATIC) ?
+ GNUNET_CONTAINER_SLIST_DISPOSITION_STATIC :
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ i.elem->elem, i.elem->len);
+ }
+ GNUNET_CONTAINER_slist_iter_destroy (&i);
+}
+
+
+/**
+ * Create a new singly linked list
+ * @return the new list
+ */
+struct GNUNET_CONTAINER_SList *
+GNUNET_CONTAINER_slist_create ()
+{
+ return GNUNET_malloc (sizeof (struct GNUNET_CONTAINER_SList));
+}
+
+
+/**
+ * Destroy a singly linked list
+ * @param l the list to be destroyed
+ */
+void
+GNUNET_CONTAINER_slist_destroy (struct GNUNET_CONTAINER_SList *l)
+{
+ GNUNET_CONTAINER_slist_clear (l);
+ GNUNET_free (l);
+}
+
+
+/**
+ * Return the beginning of a list
+ * @param l list
+ * @return iterator pointing to the beginning
+ */
+struct GNUNET_CONTAINER_SList_Iterator
+GNUNET_CONTAINER_slist_begin (struct GNUNET_CONTAINER_SList *l)
+{
+ struct GNUNET_CONTAINER_SList_Iterator ret;
+
+ memset (&ret, 0, sizeof (ret));
+ ret.elem = l->head;
+ ret.list = l;
+ return ret;
+}
+
+
+/**
+ * Clear a list
+ * @param l list
+ */
+void
+GNUNET_CONTAINER_slist_clear (struct GNUNET_CONTAINER_SList *l)
+{
+ struct GNUNET_CONTAINER_SList_Elem *e;
+ struct GNUNET_CONTAINER_SList_Elem *n;
+
+ e = l->head;
+ while (e != NULL)
+ {
+ n = e->next;
+ if (e->disp == GNUNET_CONTAINER_SLIST_DISPOSITION_DYNAMIC)
+ GNUNET_free (e->elem);
+ GNUNET_free (e);
+ e = n;
+ }
+ l->head = NULL;
+ l->tail = NULL;
+ l->length = 0;
+}
+
+
+/**
+ * Check if a list contains a certain element
+ *
+ * @param l list
+ * @param buf payload buffer to find
+ * @param len length of the payload (number of bytes in buf)
+ */
+int
+GNUNET_CONTAINER_slist_contains (const struct GNUNET_CONTAINER_SList *l,
+ const void *buf, size_t len)
+{
+ struct GNUNET_CONTAINER_SList_Elem *e;
+
+ for (e = l->head; e != NULL; e = e->next)
+ if ((e->len == len) && (memcmp (buf, e->elem, len) == 0))
+ return GNUNET_YES;
+ return GNUNET_NO;
+}
+
+
+/**
+ * Count the elements of a list
+ * @param l list
+ * @return number of elements in the list
+ */
+int
+GNUNET_CONTAINER_slist_count (const struct GNUNET_CONTAINER_SList *l)
+{
+ return l->length;
+}
+
+
+/**
+ * Remove an element from the list
+ *
+ * @param i iterator that points to the element to be removed
+ */
+void
+GNUNET_CONTAINER_slist_erase (struct GNUNET_CONTAINER_SList_Iterator *i)
+{
+ struct GNUNET_CONTAINER_SList_Elem *next;
+
+ next = i->elem->next;
+ if (i->last != NULL)
+ i->last->next = next;
+ else
+ i->list->head = next;
+ if (next == NULL)
+ i->list->tail = i->last;
+ if (i->elem->disp == GNUNET_CONTAINER_SLIST_DISPOSITION_DYNAMIC)
+ GNUNET_free (i->elem->elem);
+ GNUNET_free (i->elem);
+ i->list->length--;
+ i->elem = next;
+}
+
+
+/**
+ * Insert an element into a list at a specific position
+ * @param before where to insert the new element
+ * @param disp memory disposition
+ * @param buf payload buffer
+ * @param len length of the payload
+ */
+void
+GNUNET_CONTAINER_slist_insert (struct GNUNET_CONTAINER_SList_Iterator *before,
+ enum GNUNET_CONTAINER_SListDisposition disp,
+ const void *buf, size_t len)
+{
+ struct GNUNET_CONTAINER_SList_Elem *e;
+
+ e = create_elem (disp, buf, len);
+ e->next = before->elem;
+ if (before->last != NULL)
+ before->last->next = e;
+ else
+ before->list->head = e;
+ if (e->next == NULL)
+ before->list->tail = e;
+ before->list->length++;
+}
+
+
+/**
+ * Advance an iterator to the next element
+ * @param i iterator
+ * @return GNUNET_YES on success, GNUNET_NO if the end has been reached
+ */
+int
+GNUNET_CONTAINER_slist_next (struct GNUNET_CONTAINER_SList_Iterator *i)
+{
+ i->last = i->elem;
+ i->elem = i->elem->next;
+
+ return (i->elem != NULL) ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
+ * Check if an iterator points beyond the end of a list
+ *
+ * @param i iterator
+ * @return GNUNET_YES if the end has been reached, GNUNET_NO if the iterator
+ * points to a valid element
+ */
+int
+GNUNET_CONTAINER_slist_end (struct GNUNET_CONTAINER_SList_Iterator *i)
+{
+ return (i->elem == NULL) ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
+ * Retrieve the element at a specific position in a list
+ * @param i iterator
+ * @param len payload length
+ * @return payload
+ */
+void *
+GNUNET_CONTAINER_slist_get (const struct GNUNET_CONTAINER_SList_Iterator *i,
+ size_t * len)
+{
+ if (len)
+ *len = i->elem->len;
+ return i->elem->elem;
+}
+
+/**
+ * Release an iterator
+ * @param i iterator
+ */
+void
+GNUNET_CONTAINER_slist_iter_destroy (struct GNUNET_CONTAINER_SList_Iterator *i)
+{
+}
+
+/* end of container_slist.c */
diff --git a/src/util/crypto_aes.c b/src/util/crypto_aes.c
new file mode 100644
index 0000000..8b031f3
--- /dev/null
+++ b/src/util/crypto_aes.c
@@ -0,0 +1,185 @@
+/*
+ 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 <gcrypt.h>
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * 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_op (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
+ */
+ssize_t
+GNUNET_CRYPTO_aes_encrypt (const void *block, size_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 -1;
+ }
+ 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 block the data to decrypt, encoded as returned by encrypt
+ * @param size the size of the block to decrypt
+ * @param sessionkey the key used 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
+ */
+ssize_t
+GNUNET_CRYPTO_aes_decrypt (const void *block, size_t size,
+ 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 -1;
+ }
+ 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;
+}
+
+/**
+ * @brief Derive an IV
+ * @param iv initialization vector
+ * @param skey session key
+ * @param salt salt for the derivation
+ * @param salt_len size of the salt
+ * @param ... pairs of void * & size_t for context chunks, terminated by NULL
+ */
+void
+GNUNET_CRYPTO_aes_derive_iv (struct GNUNET_CRYPTO_AesInitializationVector *iv,
+ const struct GNUNET_CRYPTO_AesSessionKey *skey,
+ const void *salt, size_t salt_len, ...)
+{
+ va_list argp;
+
+ va_start (argp, salt_len);
+ GNUNET_CRYPTO_aes_derive_iv_v (iv, skey, salt, salt_len, argp);
+ va_end (argp);
+}
+
+/**
+ * @brief Derive an IV
+ * @param iv initialization vector
+ * @param skey session key
+ * @param salt salt for the derivation
+ * @param salt_len size of the salt
+ * @param argp pairs of void * & size_t for context chunks, terminated by NULL
+ */
+void
+GNUNET_CRYPTO_aes_derive_iv_v (struct GNUNET_CRYPTO_AesInitializationVector *iv,
+ const struct GNUNET_CRYPTO_AesSessionKey *skey,
+ const void *salt, size_t salt_len, va_list argp)
+{
+ GNUNET_CRYPTO_kdf_v (iv->iv, sizeof (iv->iv), salt, salt_len, skey->key,
+ sizeof (skey->key), argp);
+}
+
+/* end of crypto_aes.c */
diff --git a/src/util/crypto_crc.c b/src/util/crypto_crc.c
new file mode 100644
index 0000000..8bccf6b
--- /dev/null
+++ b/src/util/crypto_crc.c
@@ -0,0 +1,171 @@
+/*
+ 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-32 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 CRC16 and CRC32
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_crypto_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/* 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.
+ */
+static void
+crc_init ()
+{
+ static int once;
+ unsigned int i, j;
+ uLong h = 1;
+
+ if (once)
+ return;
+ once = 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)
+{
+ crc_init ();
+ 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
+ */
+int32_t
+GNUNET_CRYPTO_crc32_n (const void *buf, size_t len)
+{
+ uLong crc;
+
+ crc = crc32 (0L, Z_NULL, 0);
+ crc = crc32 (crc, (char *) buf, len);
+ return crc;
+}
+
+
+/**
+ * Perform an incremental step in a CRC16 (for TCP/IP) calculation.
+ *
+ * @param sum current sum, initially 0
+ * @param buf buffer to calculate CRC over (must be 16-bit aligned)
+ * @param len number of bytes in hdr, must be multiple of 2
+ * @return updated crc sum (must be subjected to GNUNET_CRYPTO_crc16_finish to get actual crc16)
+ */
+uint32_t
+GNUNET_CRYPTO_crc16_step (uint32_t sum, const void *buf, size_t len)
+{
+ const uint16_t *hdr = buf;
+ for (; len >= 2; len -= 2)
+ sum += *(hdr++);
+ if (len == 1)
+ sum += (*hdr) & ntohs(0xFF00);
+ return sum;
+}
+
+
+/**
+ * Convert results from GNUNET_CRYPTO_crc16_step to final crc16.
+ *
+ * @param sum cummulative sum
+ * @return crc16 value
+ */
+uint16_t
+GNUNET_CRYPTO_crc16_finish (uint32_t sum)
+{
+ sum = (sum >> 16) + (sum & 0xFFFF);
+ sum += (sum >> 16);
+
+ return ~sum;
+}
+
+
+/**
+ * Calculate the checksum of a buffer in one step.
+ *
+ * @param buf buffer to calculate CRC over (must be 16-bit aligned)
+ * @param len number of bytes in hdr, must be multiple of 2
+ * @return crc16 value
+ */
+uint16_t
+GNUNET_CRYPTO_crc16_n (const void *buf, size_t len)
+{
+ const uint16_t *hdr = buf;
+ uint32_t sum = GNUNET_CRYPTO_crc16_step (0, hdr, len);
+
+ return GNUNET_CRYPTO_crc16_finish (sum);
+}
+
+
+
+/* end of crypto_crc.c */
diff --git a/src/util/crypto_hash.c b/src/util/crypto_hash.c
new file mode 100644
index 0000000..63d9654
--- /dev/null
+++ b/src/util/crypto_hash.c
@@ -0,0 +1,647 @@
+/*
+ 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 <jlcooke@certainkey.com>
+
+ Copyright (c) Jean-Luc Cooke <jlcooke@certainkey.com>
+ Copyright (c) Andrew McDonald <andrew@mcdonald.org.uk>
+ Copyright (c) 2003 Kyle McMartin <kyle@debian.org>
+*/
+
+/**
+ * @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"
+#include <gcrypt.h>
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * 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, size_t size, GNUNET_HashCode * ret)
+{
+ gcry_md_hash_buffer (GCRY_MD_SHA512, ret, block, size);
+}
+
+
+/**
+ * Context used when hashing a file.
+ */
+struct GNUNET_CRYPTO_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;
+
+ /**
+ * File descriptor.
+ */
+ struct GNUNET_DISK_FileHandle *fh;
+
+ /**
+ * Cummulated hash.
+ */
+ gcry_md_hd_t md;
+
+ /**
+ * Size of the file.
+ */
+ uint64_t fsize;
+
+ /**
+ * Current offset.
+ */
+ uint64_t offset;
+
+ /**
+ * Current task for hashing.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier task;
+
+ /**
+ * Priority we use.
+ */
+ enum GNUNET_SCHEDULER_Priority priority;
+
+ /**
+ * Blocksize.
+ */
+ size_t bsize;
+
+};
+
+
+/**
+ * Report result of hash computation to callback
+ * and free associated resources.
+ */
+static void
+file_hash_finish (struct GNUNET_CRYPTO_FileHashContext *fhc,
+ const GNUNET_HashCode * res)
+{
+ fhc->callback (fhc->callback_cls, res);
+ GNUNET_free (fhc->filename);
+ if (!GNUNET_DISK_handle_invalid (fhc->fh))
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fhc->fh));
+ gcry_md_close (fhc->md);
+ 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 GNUNET_CRYPTO_FileHashContext *fhc = cls;
+ GNUNET_HashCode *res;
+ size_t delta;
+
+ fhc->task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_assert (fhc->offset <= fhc->fsize);
+ delta = fhc->bsize;
+ if (fhc->fsize - fhc->offset < delta)
+ delta = fhc->fsize - fhc->offset;
+ if (delta != GNUNET_DISK_file_read (fhc->fh, fhc->buffer, delta))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "read", fhc->filename);
+ file_hash_finish (fhc, NULL);
+ return;
+ }
+ gcry_md_write (fhc->md, fhc->buffer, delta);
+ fhc->offset += delta;
+ if (fhc->offset == fhc->fsize)
+ {
+ res = (GNUNET_HashCode *) gcry_md_read (fhc->md, GCRY_MD_SHA512);
+ file_hash_finish (fhc, res);
+ return;
+ }
+ fhc->task = GNUNET_SCHEDULER_add_with_priority (fhc->priority,
+ &file_hash_task, fhc);
+}
+
+
+/**
+ * Compute the hash of an entire file.
+ *
+ * @param priority scheduling priority to use
+ * @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
+ * @return NULL on (immediate) errror
+ */
+struct GNUNET_CRYPTO_FileHashContext *
+GNUNET_CRYPTO_hash_file (enum GNUNET_SCHEDULER_Priority priority,
+ const char *filename, size_t blocksize,
+ GNUNET_CRYPTO_HashCompletedCallback callback,
+ void *callback_cls)
+{
+ struct GNUNET_CRYPTO_FileHashContext *fhc;
+
+ GNUNET_assert (blocksize > 0);
+ fhc =
+ GNUNET_malloc (sizeof (struct GNUNET_CRYPTO_FileHashContext) + blocksize);
+ fhc->callback = callback;
+ fhc->callback_cls = callback_cls;
+ fhc->buffer = (unsigned char *) &fhc[1];
+ fhc->filename = GNUNET_strdup (filename);
+ if (GPG_ERR_NO_ERROR != gcry_md_open (&fhc->md, GCRY_MD_SHA512, 0))
+ {
+ GNUNET_break (0);
+ GNUNET_free (fhc);
+ return NULL;
+ }
+ fhc->bsize = blocksize;
+ if (GNUNET_OK != GNUNET_DISK_file_size (filename, &fhc->fsize, GNUNET_NO))
+ {
+ GNUNET_free (fhc->filename);
+ GNUNET_free (fhc);
+ return NULL;
+ }
+ fhc->fh =
+ GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (!fhc->fh)
+ {
+ GNUNET_free (fhc->filename);
+ GNUNET_free (fhc);
+ return NULL;
+ }
+ fhc->priority = priority;
+ fhc->task =
+ GNUNET_SCHEDULER_add_with_priority (priority, &file_hash_task, fhc);
+ return fhc;
+}
+
+
+/**
+ * Cancel a file hashing operation.
+ *
+ * @param fhc operation to cancel (callback must not yet have been invoked)
+ */
+void
+GNUNET_CRYPTO_hash_file_cancel (struct GNUNET_CRYPTO_FileHashContext *fhc)
+{
+ GNUNET_SCHEDULER_cancel (fhc->task);
+ GNUNET_free (fhc->filename);
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_file_close (fhc->fh));
+ GNUNET_free (fhc);
+}
+
+
+
+/* ***************** binary-ASCII encoding *************** */
+
+/**
+ * Get the numeric value corresponding to a character.
+ *
+ * @param a a character
+ * @return corresponding numeric value
+ */
+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 103
+ * characters plus a null terminator.
+ *
+ * @param block the 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)
+{
+ /**
+ * 32 characters for encoding (GNUNET_CRYPTO_hash => 32 characters)
+ */
+ static char *encTable__ = "0123456789ABCDEFGHIJKLMNOPQRSTUV";
+ 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 enclen number of characters in 'enc' (without 0-terminator, which can be missing)
+ * @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_string2 (const char *enc, size_t enclen,
+ GNUNET_HashCode * result)
+{
+ unsigned int rpos;
+ unsigned int wpos;
+ unsigned int bits;
+ unsigned int vbit;
+ int ret;
+
+ if (enclen != sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1)
+ return GNUNET_SYSERR;
+
+ vbit = 2; /* padding! */
+ wpos = sizeof (GNUNET_HashCode);
+ rpos = sizeof (struct GNUNET_CRYPTO_HashAsciiEncoded) - 1;
+ bits = (ret = getValue__ (enc[--rpos])) >> 3;
+ if (-1 == ret)
+ return GNUNET_SYSERR;
+ while (wpos > 0)
+ {
+ GNUNET_assert (rpos > 0);
+ bits = ((ret = getValue__ (enc[--rpos])) << vbit) | bits;
+ if (-1 == ret)
+ return GNUNET_SYSERR;
+ 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.
+ *
+ * @param a some hash code
+ * @param b some hash code
+ * @return 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);
+}
+
+
+/**
+ * Create a random hash code.
+ *
+ * @param mode desired quality level
+ * @param result hash code that is randomized
+ */
+void
+GNUNET_CRYPTO_hash_create_random (enum GNUNET_CRYPTO_Quality mode,
+ GNUNET_HashCode * result)
+{
+ int i;
+
+ for (i = (sizeof (GNUNET_HashCode) / sizeof (uint32_t)) - 1; i >= 0; i--)
+ result->bits[i] = GNUNET_CRYPTO_random_u32 (mode, UINT32_MAX);
+}
+
+
+/**
+ * compute result(delta) = b - a
+ *
+ * @param a some hash code
+ * @param b some hash code
+ * @param result set to b - a
+ */
+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];
+}
+
+
+/**
+ * compute result(b) = a + delta
+ *
+ * @param a some hash code
+ * @param delta some hash code
+ * @param result set to a + delta
+ */
+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];
+}
+
+
+/**
+ * compute result = a ^ b
+ *
+ * @param a some hash code
+ * @param b some hash code
+ * @param result set to a ^ b
+ */
+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.
+ *
+ * @param hc hash code that serves to generate the key
+ * @param skey set to a valid session key
+ * @param iv set to a valid initialization vector
+ */
+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;
+}
+
+
+/**
+ * Determine how many low order bits match in two
+ * GNUNET_HashCodes. i.e. - 010011 and 011111 share
+ * the first two lowest order bits, and therefore the
+ * return value is two (NOT XOR distance, nor how many
+ * bits match absolutely!).
+ *
+ * @param first the first hashcode
+ * @param second the hashcode to compare first to
+ *
+ * @return the number of bits that match
+ */
+unsigned int
+GNUNET_CRYPTO_hash_matching_bits (const GNUNET_HashCode * first,
+ const GNUNET_HashCode * second)
+{
+ unsigned int i;
+
+ for (i = 0; i < sizeof (GNUNET_HashCode) * 8; i++)
+ if (GNUNET_CRYPTO_hash_get_bit (first, i) !=
+ GNUNET_CRYPTO_hash_get_bit (second, i))
+ return i;
+ return sizeof (GNUNET_HashCode) * 8;
+}
+
+
+/**
+ * Compare function for HashCodes, producing a total ordering
+ * of all hashcodes.
+ *
+ * @param h1 some hash code
+ * @param h2 some hash code
+ * @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).
+ *
+ * @param h1 some hash code
+ * @param h2 some hash code
+ * @param target some hash code
+ * @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;
+}
+
+
+/**
+ * @brief Derive an authentication key
+ * @param key authentication key
+ * @param rkey root key
+ * @param salt salt
+ * @param salt_len size of the salt
+ * @param ... pair of void * & size_t for context chunks, terminated by NULL
+ */
+void
+GNUNET_CRYPTO_hmac_derive_key (struct GNUNET_CRYPTO_AuthKey *key,
+ const struct GNUNET_CRYPTO_AesSessionKey *rkey,
+ const void *salt, size_t salt_len, ...)
+{
+ va_list argp;
+
+ va_start (argp, salt_len);
+ GNUNET_CRYPTO_hmac_derive_key_v (key, rkey, salt, salt_len, argp);
+ va_end (argp);
+}
+
+
+/**
+ * @brief Derive an authentication key
+ * @param key authentication key
+ * @param rkey root key
+ * @param salt salt
+ * @param salt_len size of the salt
+ * @param argp pair of void * & size_t for context chunks, terminated by NULL
+ */
+void
+GNUNET_CRYPTO_hmac_derive_key_v (struct GNUNET_CRYPTO_AuthKey *key,
+ const struct GNUNET_CRYPTO_AesSessionKey *rkey,
+ const void *salt, size_t salt_len,
+ va_list argp)
+{
+ GNUNET_CRYPTO_kdf_v (key->key, sizeof (key->key), salt, salt_len, rkey->key,
+ sizeof (rkey->key), argp);
+}
+
+
+/**
+ * Calculate HMAC of a message (RFC 2104)
+ *
+ * @param key secret key
+ * @param plaintext input plaintext
+ * @param plaintext_len length of plaintext
+ * @param hmac where to store the hmac
+ */
+void
+GNUNET_CRYPTO_hmac (const struct GNUNET_CRYPTO_AuthKey *key,
+ const void *plaintext, size_t plaintext_len,
+ GNUNET_HashCode * hmac)
+{
+ gcry_md_hd_t md;
+ const unsigned char *mc;
+
+ GNUNET_assert (GPG_ERR_NO_ERROR ==
+ gcry_md_open (&md, GCRY_MD_SHA512, GCRY_MD_FLAG_HMAC));
+ gcry_md_setkey (md, key->key, sizeof (key->key));
+ gcry_md_write (md, plaintext, plaintext_len);
+ mc = gcry_md_read (md, GCRY_MD_SHA512);
+ if (mc != NULL)
+ memcpy (hmac->bits, mc, sizeof (hmac->bits));
+ gcry_md_close (md);
+}
+
+
+/* end of crypto_hash.c */
diff --git a/src/util/crypto_hkdf.c b/src/util/crypto_hkdf.c
new file mode 100644
index 0000000..40bfa67
--- /dev/null
+++ b/src/util/crypto_hkdf.c
@@ -0,0 +1,299 @@
+/*
+ Copyright (c) 2010 Nils Durner
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/**
+ * @file src/util/crypto_hkdf.c
+ * @brief Hash-based KDF as defined in RFC 5869
+ * @see http://www.rfc-editor.org/rfc/rfc5869.txt
+ * @todo remove GNUNET references
+ * @author Nils Durner
+ *
+ * The following list of people have reviewed this code and considered
+ * it correct on the date given (if you reviewed it, please
+ * have your name added to the list):
+ *
+ * - Christian Grothoff (08.10.2010)
+ * - Nathan Evans (08.10.2010)
+ * - Matthias Wachs (08.10.2010)
+ */
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Set this to 0 if you compile this code outside of GNUnet.
+ */
+#define GNUNET_BUILD 1
+
+/**
+ * Enable debugging.
+ */
+#define DEBUG_HKDF 0
+
+
+#if GNUNET_BUILD
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#else
+#define GNUNET_NO 0
+#define GNUNET_YES 1
+#define GNUNET_SYSERR -1
+#include <stdlib.h>
+#endif
+
+#include <gcrypt.h>
+
+
+/**
+ * @brief Compute the HMAC
+ * @todo use chunked buffers
+ * @param mac gcrypt MAC handle
+ * @param key HMAC key
+ * @param key_len length of key
+ * @param buf message to be processed
+ * @param buf_len length of buf
+ * @return HMAC, freed by caller via gcry_md_close/_reset
+ */
+static const void *
+doHMAC (gcry_md_hd_t mac, const void *key, size_t key_len, const void *buf,
+ size_t buf_len)
+{
+ gcry_md_setkey (mac, key, key_len);
+ gcry_md_write (mac, buf, buf_len);
+
+ return (const void *) gcry_md_read (mac, 0);
+}
+
+/**
+ * @brief Generate pseudo-random key
+ * @param mac gcrypt HMAC handle
+ * @param xts salt
+ * @param xts_len length of the salt
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @param prk result buffer (allocated by caller; at least gcry_md_dlen() bytes)
+ * @return GNUNET_YES on success
+ */
+static int
+getPRK (gcry_md_hd_t mac, const void *xts, size_t xts_len, const void *skm,
+ size_t skm_len, void *prk)
+{
+ const void *ret;
+
+ ret = doHMAC (mac, xts, xts_len, skm, skm_len);
+ if (ret == NULL)
+ return GNUNET_SYSERR;
+ memcpy (prk, ret, gcry_md_get_algo_dlen (gcry_md_get_algo (mac)));
+
+ return GNUNET_YES;
+}
+
+
+#if DEBUG_HKDF
+static void
+dump (const char *src, const void *p, unsigned int l)
+{
+ unsigned int i;
+
+ printf ("\n%s: ", src);
+ for (i = 0; i < l; i++)
+ {
+ printf ("%2x", (int) ((const unsigned char *) p)[i]);
+ }
+ printf ("\n");
+}
+#endif
+
+
+/**
+ * @brief Derive key
+ * @param result buffer for the derived key, allocated by caller
+ * @param out_len desired length of the derived key
+ * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
+ * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
+ * @param xts salt
+ * @param xts_len length of xts
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @param argp va_list of void * & size_t pairs for context chunks
+ * @return GNUNET_YES on success
+ */
+int
+GNUNET_CRYPTO_hkdf_v (void *result, size_t out_len, int xtr_algo, int prf_algo,
+ const void *xts, size_t xts_len, const void *skm,
+ size_t skm_len, va_list argp)
+{
+ const void *hc;
+ unsigned long i, t, d;
+ unsigned int k = gcry_md_get_algo_dlen (prf_algo);
+ unsigned int xtr_len = gcry_md_get_algo_dlen (xtr_algo);
+ char prk[xtr_len];
+ int ret;
+ gcry_md_hd_t xtr, prf;
+ size_t ctx_len;
+ va_list args;
+
+ if (k == 0)
+ return GNUNET_SYSERR;
+
+ if (gcry_md_open (&xtr, xtr_algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+ return GNUNET_SYSERR;
+
+ if (gcry_md_open (&prf, prf_algo, GCRY_MD_FLAG_HMAC) != GPG_ERR_NO_ERROR)
+ {
+ gcry_md_close (xtr);
+ return GNUNET_SYSERR;
+ }
+
+ va_copy (args, argp);
+
+ ctx_len = 0;
+ while (NULL != va_arg (args, void *))
+ ctx_len += va_arg (args, size_t);
+
+ va_end (args);
+
+ memset (result, 0, out_len);
+ if (getPRK (xtr, xts, xts_len, skm, skm_len, prk) != GNUNET_YES)
+ goto hkdf_error;
+#if DEBUG_HKDF
+ dump ("PRK", prk, xtr_len);
+#endif
+
+ t = out_len / k;
+ d = out_len % k;
+
+ /* K(1) */
+ {
+ size_t plain_len = k + ctx_len + 1;
+ char plain[plain_len];
+ const void *ctx;
+ char *dst;
+
+ dst = plain + k;
+ va_copy (args, argp);
+ while ((ctx = va_arg (args, void *)))
+ {
+ size_t len;
+
+ len = va_arg (args, size_t);
+ memcpy (dst, ctx, len);
+ dst += len;
+ }
+ va_end (args);
+
+ if (t > 0)
+ {
+ memset (plain + k + ctx_len, 1, 1);
+#if DEBUG_HKDF
+ dump ("K(1)", plain, plain_len);
+#endif
+ hc = doHMAC (prf, prk, xtr_len, &plain[k], ctx_len + 1);
+ if (hc == NULL)
+ goto hkdf_error;
+ memcpy (result, hc, k);
+ result += k;
+ }
+
+ /* K(i+1) */
+ for (i = 1; i < t; i++)
+ {
+ memcpy (plain, result - k, k);
+ memset (plain + k + ctx_len, i + 1, 1);
+ gcry_md_reset (prf);
+#if DEBUG_HKDF
+ dump ("K(i+1)", plain, plain_len);
+#endif
+ hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
+ if (hc == NULL)
+ goto hkdf_error;
+ memcpy (result, hc, k);
+ result += k;
+ }
+
+ /* K(t):d */
+ if (d > 0)
+ {
+ if (t > 0)
+ {
+ memcpy (plain, result - k, k);
+ i++;
+ }
+ memset (plain + k + ctx_len, i, 1);
+ gcry_md_reset (prf);
+#if DEBUG_HKDF
+ dump ("K(t):d", plain, plain_len);
+#endif
+ if (t > 0)
+ hc = doHMAC (prf, prk, xtr_len, plain, plain_len);
+ else
+ hc = doHMAC (prf, prk, xtr_len, plain + k, plain_len - k);
+ if (hc == NULL)
+ goto hkdf_error;
+ memcpy (result, hc, d);
+ }
+#if DEBUG_HKDF
+ dump ("result", result - k, out_len);
+#endif
+
+ ret = GNUNET_YES;
+ goto hkdf_ok;
+ }
+hkdf_error:
+ ret = GNUNET_SYSERR;
+hkdf_ok:
+ gcry_md_close (prf);
+ gcry_md_close (xtr);
+
+ return ret;
+}
+
+
+/**
+ * @brief Derive key
+ * @param result buffer for the derived key, allocated by caller
+ * @param out_len desired length of the derived key
+ * @param xtr_algo hash algorithm for the extraction phase, GCRY_MD_...
+ * @param prf_algo hash algorithm for the expansion phase, GCRY_MD_...
+ * @param xts salt
+ * @param xts_len length of xts
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @return GNUNET_YES on success
+ */
+int
+GNUNET_CRYPTO_hkdf (void *result, size_t out_len, int xtr_algo, int prf_algo,
+ const void *xts, size_t xts_len, const void *skm,
+ size_t skm_len, ...)
+{
+ va_list argp;
+ int ret;
+
+ va_start (argp, skm_len);
+ ret =
+ GNUNET_CRYPTO_hkdf_v (result, out_len, xtr_algo, prf_algo, xts, xts_len,
+ skm, skm_len, argp);
+ va_end (argp);
+
+ return ret;
+}
+
+/* end of crypto_hkdf.c */
diff --git a/src/util/crypto_kdf.c b/src/util/crypto_kdf.c
new file mode 100644
index 0000000..0e7fbbb
--- /dev/null
+++ b/src/util/crypto_kdf.c
@@ -0,0 +1,89 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010 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/crypto_kdf.c
+ * @brief Key derivation
+ * @author Nils Durner
+ */
+
+#include <gcrypt.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * @brief Derive key
+ * @param result buffer for the derived key, allocated by caller
+ * @param out_len desired length of the derived key
+ * @param xts salt
+ * @param xts_len length of xts
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @param argp va_list of void * & size_t pairs for context chunks
+ * @return GNUNET_YES on success
+ */
+int
+GNUNET_CRYPTO_kdf_v (void *result, size_t out_len, const void *xts,
+ size_t xts_len, const void *skm, size_t skm_len,
+ va_list argp)
+{
+ /*
+ * "Finally, we point out to a particularly advantageous instantiation using
+ * HMAC-SHA512 as XTR and HMAC-SHA256 in PRF* (in which case the output from SHA-512 is
+ * truncated to 256 bits). This makes sense in two ways: First, the extraction part is where we need a
+ * stronger hash function due to the unconventional demand from the hash function in the extraction
+ * setting. Second, as shown in Section 6, using HMAC with a truncated output as an extractor
+ * allows to prove the security of HKDF under considerably weaker assumptions on the underlying
+ * hash function."
+ *
+ * http://eprint.iacr.org/2010/264
+ */
+
+ return GNUNET_CRYPTO_hkdf_v (result, out_len, GCRY_MD_SHA512, GCRY_MD_SHA256,
+ xts, xts_len, skm, skm_len, argp);
+}
+
+/**
+ * @brief Derive key
+ * @param result buffer for the derived key, allocated by caller
+ * @param out_len desired length of the derived key
+ * @param xts salt
+ * @param xts_len length of xts
+ * @param skm source key material
+ * @param skm_len length of skm
+ * @param ... void * & size_t pairs for context chunks
+ * @return GNUNET_YES on success
+ */
+int
+GNUNET_CRYPTO_kdf (void *result, size_t out_len, const void *xts,
+ size_t xts_len, const void *skm, size_t skm_len, ...)
+{
+ va_list argp;
+ int ret;
+
+ va_start (argp, skm_len);
+ ret = GNUNET_CRYPTO_kdf_v (result, out_len, xts, xts_len, skm, skm_len, argp);
+ va_end (argp);
+
+ return ret;
+}
diff --git a/src/util/crypto_ksk.c b/src/util/crypto_ksk.c
new file mode 100644
index 0000000..0f5a295
--- /dev/null
+++ b/src/util/crypto_ksk.c
@@ -0,0 +1,769 @@
+/*
+ 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 "gnunet_os_lib.h"
+#include <gcrypt.h>
+#include <limits.h>
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * 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 { LOG(level, _("`%s' failed at %s:%d with error: %s\n"), cmd, __FILE__, __LINE__, gcry_strerror(rc)); } while(0);
+
+
+typedef struct
+{
+ gcry_mpi_t n; /* public modulus */
+ gcry_mpi_t e; /* public exponent */
+ gcry_mpi_t d; /* exponent */
+ gcry_mpi_t p; /* prime p. */
+ gcry_mpi_t q; /* prime q. */
+ gcry_mpi_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;
+};
+
+
+static void
+mpz_randomize (gcry_mpi_t n, unsigned int nbits, GNUNET_HashCode * rnd)
+{
+ GNUNET_HashCode hc;
+ GNUNET_HashCode tmp;
+ int bits_per_hc = sizeof (GNUNET_HashCode) * 8;
+ int cnt;
+ int i;
+
+ GNUNET_assert (nbits > 0);
+ cnt = (nbits + bits_per_hc - 1) / bits_per_hc;
+ gcry_mpi_set_ui (n, 0);
+
+ tmp = *rnd;
+ for (i = 0; i < cnt; i++)
+ {
+ int j;
+
+ if (i > 0)
+ GNUNET_CRYPTO_hash (&hc, sizeof (GNUNET_HashCode), &tmp);
+ for (j = 0; j < sizeof (GNUNET_HashCode) / sizeof (uint32_t); j++)
+ {
+#if HAVE_GCRY_MPI_LSHIFT
+ gcry_mpi_lshift (n, n, sizeof (uint32_t) * 8);
+#else
+ gcry_mpi_mul_ui (n, n, 1 << (sizeof (uint32_t) * 4));
+ gcry_mpi_mul_ui (n, n, 1 << (sizeof (uint32_t) * 4));
+#endif
+ gcry_mpi_add_ui (n, n, ntohl (((uint32_t *) & tmp)[j]));
+ }
+ hc = tmp;
+ }
+ GNUNET_CRYPTO_hash (&hc, sizeof (GNUNET_HashCode), rnd);
+ i = gcry_mpi_get_nbits (n);
+ while (i > nbits)
+ gcry_mpi_clear_bit (n, --i);
+}
+
+static unsigned long
+mpz_trailing_zeroes (gcry_mpi_t n)
+{
+ unsigned int idx, cnt;
+
+ cnt = gcry_mpi_get_nbits (n);
+ for (idx = 0; idx < cnt; idx++)
+ {
+ if (gcry_mpi_test_bit (n, idx) == 0)
+ return idx;
+ }
+
+ return ULONG_MAX;
+}
+
+static void
+mpz_tdiv_q_2exp (gcry_mpi_t q, gcry_mpi_t n, unsigned int b)
+{
+ gcry_mpi_t u, d;
+
+ u = gcry_mpi_set_ui (NULL, 1);
+ d = gcry_mpi_new (0);
+ gcry_mpi_mul_2exp (d, u, b);
+ gcry_mpi_div (q, NULL, n, d, 0);
+}
+
+/**
+ * Return true if n is probably a prime
+ */
+static int
+is_prime (gcry_mpi_t n, int steps, GNUNET_HashCode * hc)
+{
+ gcry_mpi_t x;
+ gcry_mpi_t y;
+ gcry_mpi_t z;
+ gcry_mpi_t nminus1;
+ gcry_mpi_t a2;
+ gcry_mpi_t q;
+ unsigned int i, j, k;
+ int rc = 0;
+ unsigned int nbits;
+
+ x = gcry_mpi_new (0);
+ y = gcry_mpi_new (0);
+ z = gcry_mpi_new (0);
+ nminus1 = gcry_mpi_new (0);
+ a2 = gcry_mpi_set_ui (NULL, 2);
+
+ nbits = gcry_mpi_get_nbits (n);
+ gcry_mpi_sub_ui (nminus1, n, 1);
+
+ /* Find q and k, so that n = 1 + 2^k * q . */
+ q = gcry_mpi_set (NULL, nminus1);
+ k = mpz_trailing_zeroes (q);
+ mpz_tdiv_q_2exp (q, q, k);
+
+ for (i = 0; i < steps; i++)
+ {
+ if (!i)
+ {
+ gcry_mpi_set_ui (x, 2);
+ }
+ else
+ {
+ mpz_randomize (x, nbits - 1, hc);
+ GNUNET_assert (gcry_mpi_cmp (x, nminus1) < 0);
+ GNUNET_assert (gcry_mpi_cmp_ui (x, 1) > 0);
+ }
+ gcry_mpi_powm (y, x, q, n);
+ if (gcry_mpi_cmp_ui (y, 1) && gcry_mpi_cmp (y, nminus1))
+ {
+ for (j = 1; j < k && gcry_mpi_cmp (y, nminus1); j++)
+ {
+ gcry_mpi_powm (y, y, a2, n);
+ if (!gcry_mpi_cmp_ui (y, 1))
+ goto leave; /* Not a prime. */
+ }
+ if (gcry_mpi_cmp (y, nminus1))
+ goto leave; /* Not a prime. */
+ }
+ }
+ rc = 1; /* May be a prime. */
+
+leave:
+ gcry_mpi_release (x);
+ gcry_mpi_release (y);
+ gcry_mpi_release (z);
+ gcry_mpi_release (nminus1);
+ gcry_mpi_release (q);
+ gcry_mpi_release (a2);
+
+ return rc;
+}
+
+/**
+ * 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);
+ }
+}
+
+
+static void
+gen_prime (gcry_mpi_t * ptest, unsigned int nbits, GNUNET_HashCode * hc)
+{
+ /* 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 const 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;
+
+ gcry_mpi_t prime, pminus1, val_2, val_3, result;
+ unsigned int i;
+ unsigned int step;
+ unsigned int mods[no_of_small_prime_numbers];
+ gcry_mpi_t tmp;
+ gcry_mpi_t sp;
+
+ GNUNET_assert (nbits >= 16);
+
+ /* Make nbits fit into mpz_t implementation. */
+ val_2 = gcry_mpi_set_ui (NULL, 2);
+ val_3 = gcry_mpi_set_ui (NULL, 3);
+ prime = gcry_mpi_snew (0);
+ result = gcry_mpi_new (0);
+ pminus1 = gcry_mpi_new (0);
+ *ptest = gcry_mpi_new (0);
+ tmp = gcry_mpi_new (0);
+ sp = gcry_mpi_new (0);
+ 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. */
+ gcry_mpi_set_bit (prime, nbits - 1);
+ gcry_mpi_set_bit (prime, nbits - 2);
+ gcry_mpi_set_bit (prime, 0);
+
+ /* Calculate all remainders. */
+ for (i = 0; i < no_of_small_prime_numbers; i++)
+ {
+ size_t written;
+
+ gcry_mpi_set_ui (sp, small_prime_numbers[i]);
+ gcry_mpi_div (NULL, tmp, prime, sp, -1);
+ mods[i] = 0;
+ written = sizeof (unsigned int);
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG,
+ (unsigned char *) &mods[i], written,
+ &written, tmp));
+ adjust ((unsigned char *) &mods[i], written, sizeof (unsigned int));
+ mods[i] = ntohl (mods[i]);
+ }
+ /* 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; i < no_of_small_prime_numbers; i++)
+ {
+ uint16_t x = small_prime_numbers[i];
+
+ while (mods[i] + step >= x)
+ mods[i] -= x;
+ if (!(mods[i] + step))
+ break;
+ }
+ if (i < no_of_small_prime_numbers)
+ continue; /* Found a multiple of an already known prime. */
+
+ gcry_mpi_add_ui (*ptest, prime, step);
+ if (!gcry_mpi_test_bit (*ptest, nbits - 2))
+ break;
+
+ /* Do a fast Fermat test now. */
+ gcry_mpi_sub_ui (pminus1, *ptest, 1);
+ gcry_mpi_powm (result, val_2, pminus1, *ptest);
+ if ((!gcry_mpi_cmp_ui (result, 1)) && (is_prime (*ptest, 5, hc)))
+ {
+ /* Got it. */
+ gcry_mpi_release (sp);
+ gcry_mpi_release (tmp);
+ gcry_mpi_release (val_2);
+ gcry_mpi_release (val_3);
+ gcry_mpi_release (result);
+ gcry_mpi_release (pminus1);
+ gcry_mpi_release (prime);
+ return;
+ }
+ }
+ }
+}
+
+/**
+ * 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)
+{
+ gcry_mpi_t t1, t2;
+ gcry_mpi_t phi; /* helper: (p-1)(q-1) */
+ gcry_mpi_t g;
+ gcry_mpi_t f;
+
+ /* make sure that nbits is even so that we generate p, q of equal size */
+ if ((nbits & 1))
+ nbits++;
+
+ sk->e = gcry_mpi_set_ui (NULL, 257);
+ sk->n = gcry_mpi_new (0);
+ sk->p = gcry_mpi_new (0);
+ sk->q = gcry_mpi_new (0);
+ sk->d = gcry_mpi_new (0);
+ sk->u = gcry_mpi_new (0);
+
+ t1 = gcry_mpi_new (0);
+ t2 = gcry_mpi_new (0);
+ phi = gcry_mpi_new (0);
+ g = gcry_mpi_new (0);
+ f = gcry_mpi_new (0);
+
+ do
+ {
+ do
+ {
+ gcry_mpi_release (sk->p);
+ gcry_mpi_release (sk->q);
+ gen_prime (&sk->p, nbits / 2, hc);
+ gen_prime (&sk->q, nbits / 2, hc);
+
+ if (gcry_mpi_cmp (sk->p, sk->q) > 0) /* p shall be smaller than q (for calc of u) */
+ gcry_mpi_swap (sk->p, sk->q);
+ /* calculate the modulus */
+ gcry_mpi_mul (sk->n, sk->p, sk->q);
+ }
+ while (gcry_mpi_get_nbits (sk->n) != nbits);
+
+ /* calculate Euler totient: phi = (p-1)(q-1) */
+ gcry_mpi_sub_ui (t1, sk->p, 1);
+ gcry_mpi_sub_ui (t2, sk->q, 1);
+ gcry_mpi_mul (phi, t1, t2);
+ gcry_mpi_gcd (g, t1, t2);
+ gcry_mpi_div (f, NULL, phi, g, 0);
+ while (0 == gcry_mpi_gcd (t1, sk->e, phi))
+ { /* (while gcd is not 1) */
+ gcry_mpi_add_ui (sk->e, sk->e, 2);
+ }
+
+ /* calculate the secret key d = e^1 mod phi */
+ }
+ while ((0 == gcry_mpi_invm (sk->d, sk->e, f)) ||
+ (0 == gcry_mpi_invm (sk->u, sk->p, sk->q)));
+
+ gcry_mpi_release (t1);
+ gcry_mpi_release (t2);
+ gcry_mpi_release (phi);
+ gcry_mpi_release (f);
+ gcry_mpi_release (g);
+}
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * 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 */
+};
+GNUNET_NETWORK_STRUCT_END
+
+/**
+ * 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;
+ unsigned char *pbu[6];
+ gcry_mpi_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++)
+ {
+ gcry_mpi_aprint (GCRYMPI_FMT_STD, &pbu[i], &sizes[i], *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++)
+ {
+ gcry_mpi_release (*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;
+}
+
+
+struct KBlockKeyCacheLine
+{
+ GNUNET_HashCode hc;
+ struct KskRsaPrivateKeyBinaryEncoded *pke;
+};
+
+static struct 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;
+ struct KBlockKeyCacheLine *line;
+ unsigned 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 (struct 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__ ((destructor)) GNUNET_CRYPTO_ksk_fini ()
+{
+ unsigned 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 crypto_ksk.c */
diff --git a/src/util/crypto_random.c b/src/util/crypto_random.c
new file mode 100644
index 0000000..25226a3
--- /dev/null
+++ b/src/util/crypto_random.c
@@ -0,0 +1,334 @@
+/*
+ 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 "gnunet_os_lib.h"
+#include <gcrypt.h>
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+/* TODO: ndurner, move this to plibc? */
+/* The code is derived from glibc, obviously */
+#if MINGW
+#ifdef RANDOM
+#undef RANDOM
+#endif
+#ifdef SRANDOM
+#undef SRANDOM
+#endif
+#define RANDOM() glibc_weak_rand32()
+#define SRANDOM(s) glibc_weak_srand32(s)
+static int32_t glibc_weak_rand32_state = 1;
+
+void
+glibc_weak_srand32 (int32_t s)
+{
+ glibc_weak_rand32_state = s;
+}
+
+int32_t
+glibc_weak_rand32 ()
+{
+ int32_t val = glibc_weak_rand32_state;
+
+ val = ((glibc_weak_rand32_state * 1103515245) + 12345) & 0x7fffffff;
+ glibc_weak_rand32_state = val;
+ return val;
+}
+#endif
+
+/**
+ * Create a cryptographically weak pseudo-random number in the interval of 0 to 1.
+ *
+ * @return number between 0 and 1.
+ */
+static double
+weak_random ()
+{
+ return ((double) RANDOM () / RAND_MAX);
+}
+
+/**
+ * Seed a weak random generator. Only GNUNET_CRYPTO_QUALITY_WEAK-mode generator
+ * can be seeded.
+ *
+ * @param seed the seed to use
+ */
+void
+GNUNET_CRYPTO_seed_weak_random (int32_t seed)
+{
+ SRANDOM (seed);
+}
+
+/**
+ * Produce a random value.
+ *
+ * @param mode desired quality of the random number
+ * @param i the upper limit (exclusive) for the random number
+ * @return a random value in the interval [0,i[.
+ */
+uint32_t
+GNUNET_CRYPTO_random_u32 (enum GNUNET_CRYPTO_Quality mode, uint32_t i)
+{
+#ifdef gcry_fast_random_poll
+ static unsigned int invokeCount;
+#endif
+ uint32_t ret;
+ uint32_t ul;
+
+ GNUNET_assert (i > 0);
+
+ switch (mode)
+ {
+ case 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
+ ul = UINT32_MAX - (UINT32_MAX % i);
+ do
+ {
+ gcry_randomize ((unsigned char *) &ret, sizeof (uint32_t),
+ GCRY_STRONG_RANDOM);
+ }
+ while (ret >= ul);
+ return ret % i;
+ case GNUNET_CRYPTO_QUALITY_NONCE:
+ ul = UINT32_MAX - (UINT32_MAX % i);
+ do
+ {
+ gcry_create_nonce (&ret, sizeof (ret));
+ }
+ while (ret >= ul);
+ return ret % i;
+ case GNUNET_CRYPTO_QUALITY_WEAK:
+ ret = i * weak_random ();
+ if (ret >= i)
+ ret = i - 1;
+ return ret;
+ default:
+ GNUNET_assert (0);
+ }
+ return 0;
+}
+
+
+/**
+ * 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;
+ uint32_t x;
+
+ GNUNET_assert (n > 0);
+ ret = GNUNET_malloc (n * sizeof (unsigned int));
+ for (i = 0; i < n; i++)
+ ret[i] = i;
+ for (i = n - 1; i > 0; i--)
+ {
+ x = GNUNET_CRYPTO_random_u32 (mode, i + 1);
+ tmp = ret[x];
+ ret[x] = ret[i];
+ ret[i] = tmp;
+ }
+ return ret;
+}
+
+/**
+ * Random on unsigned 64-bit values.
+ *
+ *
+ * @param mode desired quality of the random number
+ * @param max value returned will be in range [0,max) (exclusive)
+ * @return random 64-bit number
+ */
+uint64_t
+GNUNET_CRYPTO_random_u64 (enum GNUNET_CRYPTO_Quality mode, uint64_t max)
+{
+ uint64_t ret;
+ uint64_t ul;
+
+ GNUNET_assert (max > 0);
+ switch (mode)
+ {
+ case GNUNET_CRYPTO_QUALITY_STRONG:
+ ul = UINT64_MAX - (UINT64_MAX % max);
+ do
+ {
+ gcry_randomize ((unsigned char *) &ret, sizeof (uint64_t),
+ GCRY_STRONG_RANDOM);
+ }
+ while (ret >= ul);
+ return ret % max;
+ case GNUNET_CRYPTO_QUALITY_NONCE:
+ ul = UINT64_MAX - (UINT64_MAX % max);
+ do
+ {
+ gcry_create_nonce (&ret, sizeof (ret));
+ }
+ while (ret >= ul);
+
+ return ret % max;
+ case GNUNET_CRYPTO_QUALITY_WEAK:
+ ret = max * weak_random ();
+ if (ret >= max)
+ ret = max - 1;
+ return ret;
+ default:
+ GNUNET_assert (0);
+ }
+ return 0;
+}
+
+/**
+ * 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);
+}
+
+
+/**
+ * Process ID of the "find" process that we use for
+ * entropy gathering.
+ */
+static struct GNUNET_OS_Process *genproc;
+
+/**
+ * Function called by libgcrypt whenever we are
+ * blocked gathering entropy.
+ */
+static void
+entropy_generator (void *cls, const char *what, int printchar, int current,
+ int total)
+{
+ unsigned long code;
+ enum GNUNET_OS_ProcessStatusType type;
+ int ret;
+
+ if (0 != strcmp (what, "need_entropy"))
+ return;
+ if (current == total)
+ {
+ if (genproc != NULL)
+ {
+ if (0 != GNUNET_OS_process_kill (genproc, SIGTERM))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "kill");
+ GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc));
+ GNUNET_OS_process_close (genproc);
+ genproc = NULL;
+ }
+ return;
+ }
+ if (genproc != NULL)
+ {
+ ret = GNUNET_OS_process_status (genproc, &type, &code);
+ if (ret == GNUNET_NO)
+ return; /* still running */
+ if (ret == GNUNET_SYSERR)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (0 != GNUNET_OS_process_kill (genproc, SIGTERM))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "kill");
+ GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc));
+ GNUNET_OS_process_close (genproc);
+ genproc = NULL;
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Starting `%s' process to generate entropy\n"),
+ "find");
+ genproc =
+ GNUNET_OS_start_process (GNUNET_NO,
+ NULL, NULL, "sh", "sh", "-c",
+ "exec find / -mount -type f -exec cp {} /dev/null \\; 2>/dev/null",
+ NULL);
+}
+
+
+static void
+killfind ()
+{
+ if (genproc != NULL)
+ {
+ GNUNET_OS_process_kill (genproc, SIGKILL);
+ GNUNET_OS_process_close (genproc);
+ genproc = NULL;
+ }
+}
+
+
+void __attribute__ ((constructor)) GNUNET_CRYPTO_random_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);
+ GNUNET_abort ();
+ }
+#ifdef GCRYCTL_INITIALIZATION_FINISHED
+ gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
+#endif
+#ifdef gcry_fast_random_poll
+ gcry_fast_random_poll ();
+#endif
+ gcry_set_progress_handler (&entropy_generator, NULL);
+ atexit (&killfind);
+ GNUNET_CRYPTO_seed_weak_random (time (NULL) ^
+ GNUNET_CRYPTO_random_u32
+ (GNUNET_CRYPTO_QUALITY_NONCE, UINT32_MAX));
+}
+
+
+void __attribute__ ((destructor)) GNUNET_CRYPTO_random_fini ()
+{
+ gcry_set_progress_handler (NULL, NULL);
+}
+
+
+
+/* end of crypto_random.c */
diff --git a/src/util/crypto_rsa.c b/src/util/crypto_rsa.c
new file mode 100644
index 0000000..418fe83
--- /dev/null
+++ b/src/util/crypto_rsa.c
@@ -0,0 +1,967 @@
+/*
+ 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 <gcrypt.h>
+#include "gnunet_common.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_disk_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * 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_NETWORK_STRUCT_BEGIN
+
+/**
+ * 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 */
+};
+GNUNET_NETWORK_STRUCT_END
+
+#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 { 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 priv the private key
+ * @param pub where to write the public key
+ */
+void
+GNUNET_CRYPTO_rsa_key_get_public (const struct GNUNET_CRYPTO_RsaPrivateKey
+ *priv,
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded
+ *pub)
+{
+ gcry_mpi_t skey[2];
+ size_t size;
+ int rc;
+
+ rc = key_from_sexp (skey, priv->sexp, "public-key", "ne");
+ if (rc)
+ rc = key_from_sexp (skey, priv->sexp, "private-key", "ne");
+ if (rc)
+ rc = key_from_sexp (skey, priv->sexp, "rsa", "ne");
+ GNUNET_assert (0 == rc);
+ pub->len =
+ htons (sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded) -
+ sizeof (pub->padding));
+ pub->sizen = htons (GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH);
+ pub->padding = 0;
+ size = GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH;
+ GNUNET_assert (0 ==
+ gcry_mpi_print (GCRYMPI_FMT_USG, &pub->key[0], size, &size,
+ skey[0]));
+ adjust (&pub->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,
+ &pub->key
+ [GNUNET_CRYPTO_RSA_DATA_ENCODING_LENGTH], size,
+ &size, skey[1]));
+ adjust (&pub->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.
+ *
+ * @param buf the buffer where the private key data is stored
+ * @param len the length of the data in 'buffer'
+ */
+struct GNUNET_CRYPTO_RsaPrivateKey *
+GNUNET_CRYPTO_rsa_decode_key (const char *buf, uint16_t len)
+{
+ struct GNUNET_CRYPTO_RsaPrivateKey *ret;
+ const struct RsaPrivateKeyBinaryEncoded *encoding =
+ (const struct RsaPrivateKeyBinaryEncoded *) buf;
+ gcry_sexp_t res;
+ gcry_mpi_t n, e, d, p, q, u;
+ int rc;
+ size_t size;
+ int pos;
+ uint16_t enc_len;
+
+ enc_len = ntohs (encoding->len);
+ if (len != enc_len)
+ return NULL;
+
+ 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.
+ * 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 GNUNET_CRYPTO_RsaPrivateKey *ret;
+ struct RsaPrivateKeyBinaryEncoded *enc;
+ uint16_t len;
+ struct GNUNET_DISK_FileHandle *fd;
+ unsigned int cnt;
+ int ec;
+ uint64_t fs;
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
+ struct GNUNET_PeerIdentity pid;
+
+ if (GNUNET_SYSERR == GNUNET_DISK_directory_create_for_file (filename))
+ return NULL;
+ while (GNUNET_YES != GNUNET_DISK_file_test (filename))
+ {
+ fd = GNUNET_DISK_file_open (filename,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE
+ | GNUNET_DISK_OPEN_FAILIFEXISTS,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ if (NULL == fd)
+ {
+ if (errno == EEXIST)
+ {
+ if (GNUNET_YES != GNUNET_DISK_file_test (filename))
+ {
+ /* must exist but not be accessible, fail for good! */
+ if (0 != ACCESS (filename, R_OK))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", filename);
+ else
+ GNUNET_break (0); /* what is going on!? */
+ return NULL;
+ }
+ continue;
+ }
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
+ return NULL;
+ }
+ cnt = 0;
+
+ while (GNUNET_YES !=
+ GNUNET_DISK_file_lock (fd, 0,
+ sizeof (struct RsaPrivateKeyBinaryEncoded),
+ GNUNET_YES))
+ {
+ sleep (1);
+ if (0 == ++cnt % 10)
+ {
+ ec = errno;
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not aquire lock on file `%s': %s...\n"), filename,
+ STRERROR (ec));
+ }
+ }
+ 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) ==
+ GNUNET_DISK_file_write (fd, enc, ntohs (enc->len)));
+ GNUNET_free (enc);
+
+ GNUNET_DISK_file_sync (fd);
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_unlock (fd, 0,
+ sizeof (struct RsaPrivateKeyBinaryEncoded)))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
+ GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
+ GNUNET_CRYPTO_rsa_key_get_public (ret, &pub);
+ GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("I am host `%s'. Stored new private key in `%s'.\n"),
+ GNUNET_i2s (&pid), filename);
+ return ret;
+ }
+ /* hostkey file exists already, read it! */
+ fd = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (NULL == fd)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "open", filename);
+ return NULL;
+ }
+ cnt = 0;
+ while (1)
+ {
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_lock (fd, 0,
+ sizeof (struct RsaPrivateKeyBinaryEncoded),
+ GNUNET_NO))
+ {
+ if (0 == ++cnt % 60)
+ {
+ ec = errno;
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not aquire lock on file `%s': %s...\n"), filename,
+ STRERROR (ec));
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("This may be ok if someone is currently generating a hostkey.\n"));
+ }
+ sleep (1);
+ continue;
+ }
+ if (GNUNET_YES != GNUNET_DISK_file_test (filename))
+ {
+ /* eh, what!? File we opened is now gone!? */
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", filename);
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_unlock (fd, 0,
+ sizeof (struct RsaPrivateKeyBinaryEncoded)))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fd));
+
+ return NULL;
+ }
+ if (GNUNET_YES != GNUNET_DISK_file_size (filename, &fs, GNUNET_YES))
+ fs = 0;
+ if (fs < 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! */
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_unlock (fd, 0,
+ sizeof (struct RsaPrivateKeyBinaryEncoded)))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
+ if (0 == ++cnt % 10)
+ {
+ 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) fs,
+ (unsigned int) sizeof (struct RsaPrivateKeyBinaryEncoded));
+ 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 (fs);
+ GNUNET_assert (fs == GNUNET_DISK_file_read (fd, enc, fs));
+ len = ntohs (enc->len);
+ ret = NULL;
+ if ((len != fs) ||
+ (NULL == (ret = GNUNET_CRYPTO_rsa_decode_key ((char *) enc, len))))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("File `%s' does not contain a valid private key. Deleting it.\n"),
+ filename);
+ if (0 != UNLINK (filename))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", filename);
+ }
+ }
+ GNUNET_free (enc);
+ if (GNUNET_YES !=
+ GNUNET_DISK_file_unlock (fd, 0,
+ sizeof (struct RsaPrivateKeyBinaryEncoded)))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fcntl", filename);
+ GNUNET_assert (GNUNET_YES == GNUNET_DISK_file_close (fd));
+ if (ret != NULL)
+ {
+ GNUNET_CRYPTO_rsa_key_get_public (ret, &pub);
+ GNUNET_CRYPTO_hash (&pub, sizeof (pub), &pid.hashPubKey);
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("I am host `%s'. Read private key from `%s'.\n"), GNUNET_i2s (&pid),
+ filename);
+ }
+ return ret;
+}
+
+
+/**
+ * Encrypt a block with the public key of another host that uses the
+ * same cipher.
+ *
+ * @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, size_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);
+ if (pubkey == NULL)
+ return GNUNET_SYSERR;
+ 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 key the key 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
+ * @return the size of the decrypted block, -1 on error
+ */
+ssize_t
+GNUNET_CRYPTO_rsa_decrypt (const struct GNUNET_CRYPTO_RsaPrivateKey * key,
+ const struct GNUNET_CRYPTO_RsaEncryptedData * block,
+ void *result, size_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 (key->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, key->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 key private key to use for the signing
+ * @param purpose what to sign (size, purpose)
+ * @param sig 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 *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, key->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)
+ {
+ 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 0000000..b6b458f
--- /dev/null
+++ b/src/util/disk.c
@@ -0,0 +1,2505 @@
+/*
+ This file is part of GNUnet.
+ (C) 2001--2012 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
+ * @author Nils Durner
+ */
+
+#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"
+#include "gnunet_crypto_lib.h"
+#include "disk.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+#define DEBUG_NPIPE GNUNET_EXTRA_LOGGING
+
+#define DEBUG_PIPE GNUNET_EXTRA_LOGGING
+
+/**
+ * Block size for IO for copying files.
+ */
+#define COPY_BLK_SIZE 65536
+
+
+
+#if defined(LINUX) || defined(CYGWIN)
+#include <sys/vfs.h>
+#else
+#if defined(SOMEBSD) || defined(DARWIN)
+#include <sys/param.h>
+#include <sys/mount.h>
+#else
+#ifdef SOLARIS
+#include <sys/types.h>
+#include <sys/statvfs.h>
+#else
+#ifdef MINGW
+#ifndef PIPE_BUF
+#define PIPE_BUF 512
+ULONG PipeSerialNumber;
+#endif
+#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
+
+#if !defined(SOMEBSD) && !defined(DARWIN) && !defined(WINDOWS)
+#include <wordexp.h>
+#endif
+#if LINUX
+#include <sys/statvfs.h>
+#endif
+
+
+/**
+ * Handle used to manage a pipe.
+ */
+struct GNUNET_DISK_PipeHandle
+{
+ /**
+ * File descriptors for the pipe.
+ */
+ struct GNUNET_DISK_FileHandle *fd[2];
+};
+
+
+/**
+ * Closure for the recursion to determine the file size
+ * of a directory.
+ */
+struct GetFileSizeData
+{
+ /**
+ * Set to the total file size.
+ */
+ uint64_t total;
+
+ /**
+ * GNUNET_YES if symbolic links should be included.
+ */
+ int include_sym_links;
+};
+
+
+static int
+translate_unix_perms (enum GNUNET_DISK_AccessPermissions perm)
+{
+ int mode;
+
+ mode = 0;
+ if (perm & GNUNET_DISK_PERM_USER_READ)
+ mode |= S_IRUSR;
+ if (perm & GNUNET_DISK_PERM_USER_WRITE)
+ mode |= S_IWUSR;
+ if (perm & GNUNET_DISK_PERM_USER_EXEC)
+ mode |= S_IXUSR;
+ if (perm & GNUNET_DISK_PERM_GROUP_READ)
+ mode |= S_IRGRP;
+ if (perm & GNUNET_DISK_PERM_GROUP_WRITE)
+ mode |= S_IWGRP;
+ if (perm & GNUNET_DISK_PERM_GROUP_EXEC)
+ mode |= S_IXGRP;
+ if (perm & GNUNET_DISK_PERM_OTHER_READ)
+ mode |= S_IROTH;
+ if (perm & GNUNET_DISK_PERM_OTHER_WRITE)
+ mode |= S_IWOTH;
+ if (perm & GNUNET_DISK_PERM_OTHER_EXEC)
+ mode |= S_IXOTH;
+
+ return mode;
+}
+
+
+/**
+ * Iterate over all files in the given directory and
+ * accumulate their size.
+ *
+ * @param cls closure of type "struct GetFileSizeData"
+ * @param fn current filename we are looking at
+ * @return GNUNET_SYSERR on serious errors, otherwise GNUNET_OK
+ */
+static int
+getSizeRec (void *cls, const char *fn)
+{
+ struct GetFileSizeData *gfsd = cls;
+
+#ifdef HAVE_STAT64
+ struct stat64 buf;
+#else
+ struct stat buf;
+#endif
+
+#ifdef HAVE_STAT64
+ if (0 != STAT64 (fn, &buf))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat64", fn);
+ return GNUNET_SYSERR;
+ }
+#else
+ if (0 != STAT (fn, &buf))
+ {
+ 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;
+}
+
+
+/**
+ * Checks whether a handle is invalid
+ *
+ * @param h handle to check
+ * @return GNUNET_YES if invalid, GNUNET_NO if valid
+ */
+int
+GNUNET_DISK_handle_invalid (const struct GNUNET_DISK_FileHandle *h)
+{
+#ifdef MINGW
+ return ((!h) || (h->h == INVALID_HANDLE_VALUE)) ? GNUNET_YES : GNUNET_NO;
+#else
+ return ((!h) || (h->fd == -1)) ? GNUNET_YES : GNUNET_NO;
+#endif
+}
+
+/**
+ * Get the size of an open file.
+ *
+ * @param fh open file handle
+ * @param size where to write size of the file
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_DISK_file_handle_size (struct GNUNET_DISK_FileHandle *fh,
+ OFF_T *size)
+{
+#if WINDOWS
+ BOOL b;
+ LARGE_INTEGER li;
+ b = GetFileSizeEx (fh->h, &li);
+ if (!b)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+ *size = (OFF_T) li.QuadPart;
+#else
+ struct stat sbuf;
+
+ if (0 != FSTAT (fh->fd, &sbuf))
+ return GNUNET_SYSERR;
+ *size = sbuf.st_size;
+#endif
+ return GNUNET_OK;
+}
+
+
+/**
+ * Move the read/write pointer in a file
+ *
+ * @param h handle of an open file
+ * @param offset position to move to
+ * @param whence specification to which position the offset parameter relates to
+ * @return the new position on success, GNUNET_SYSERR otherwise
+ */
+OFF_T
+GNUNET_DISK_file_seek (const struct GNUNET_DISK_FileHandle * h, OFF_T offset,
+ enum GNUNET_DISK_Seek whence)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ LARGE_INTEGER li, new_pos;
+ BOOL b;
+
+ static DWORD t[] = {[GNUNET_DISK_SEEK_SET] = FILE_BEGIN,
+ [GNUNET_DISK_SEEK_CUR] = FILE_CURRENT,[GNUNET_DISK_SEEK_END] = FILE_END
+ };
+ li.QuadPart = offset;
+
+ b = SetFilePointerEx (h->h, li, &new_pos, t[whence]);
+ if (b == 0)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+ return (OFF_T) new_pos.QuadPart;
+#else
+ static int t[] = {[GNUNET_DISK_SEEK_SET] = SEEK_SET,
+ [GNUNET_DISK_SEEK_CUR] = SEEK_CUR,[GNUNET_DISK_SEEK_END] = SEEK_END
+ };
+
+ return lseek (h->fd, offset, t[whence]);
+#endif
+}
+
+
+/**
+ * Get the size of the file (or directory) of the given file (in
+ * bytes).
+ *
+ * @param filename name of the file or directory
+ * @param size set to the size of the file (or,
+ * in the case of directories, the sum
+ * of all sizes of files in the directory)
+ * @param includeSymLinks should symbolic links be
+ * included?
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+GNUNET_DISK_file_size (const char *filename, uint64_t * size,
+ int includeSymLinks)
+{
+ struct 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;
+}
+
+
+/**
+ * Obtain some unique identifiers for the given file
+ * that can be used to identify it in the local system.
+ * This function is used between GNUnet processes to
+ * quickly check if two files with the same absolute path
+ * are actually identical. The two processes represent
+ * the same peer but may communicate over the network
+ * (and the file may be on an NFS volume). This function
+ * may not be supported on all operating systems.
+ *
+ * @param filename name of the file
+ * @param dev set to the device ID
+ * @param ino set to the inode ID
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_DISK_file_get_identifiers (const char *filename, uint64_t * dev,
+ uint64_t * ino)
+{
+#if LINUX
+ struct stat sbuf;
+ struct statvfs fbuf;
+
+ if ((0 == stat (filename, &sbuf)) && (0 == statvfs (filename, &fbuf)))
+ {
+ *dev = (uint64_t) fbuf.f_fsid;
+ *ino = (uint64_t) sbuf.st_ino;
+ return GNUNET_OK;
+ }
+#elif SOMEBSD
+ struct stat sbuf;
+ struct statfs fbuf;
+
+ if ((0 == stat (filename, &sbuf)) && (0 == statfs (filename, &fbuf)))
+ {
+ *dev = ((uint64_t) fbuf.f_fsid.val[0]) << 32 ||
+ ((uint64_t) fbuf.f_fsid.val[1]);
+ *ino = (uint64_t) sbuf.st_ino;
+ return GNUNET_OK;
+ }
+#elif WINDOWS
+ // FIXME NILS: test this
+ struct GNUNET_DISK_FileHandle *fh;
+ BY_HANDLE_FILE_INFORMATION info;
+ int succ;
+
+ fh = GNUNET_DISK_file_open (filename, GNUNET_DISK_OPEN_READ, 0);
+ if (fh == NULL)
+ return GNUNET_SYSERR;
+ succ = GetFileInformationByHandle (fh->h, &info);
+ GNUNET_DISK_file_close (fh);
+ if (succ)
+ {
+ *dev = info.dwVolumeSerialNumber;
+ *ino = ((((uint64_t) info.nFileIndexHigh) << (sizeof (DWORD) * 8)) | info.nFileIndexLow);
+ return GNUNET_OK;
+ }
+ else
+ return GNUNET_SYSERR;
+
+#endif
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Create an (empty) temporary file on disk. If the given name is not
+ * an absolute path, the current 'TMPDIR' will be prepended. In any case,
+ * 6 random characters will be appended to the name to create a unique
+ * filename.
+ *
+ * @param t component to use for the name;
+ * does NOT contain "XXXXXX" or "/tmp/".
+ * @return NULL on error, otherwise name of fresh
+ * file on disk in directory for temporary files
+ */
+char *
+GNUNET_DISK_mktemp (const char *t)
+{
+ const char *tmpdir;
+ int fd;
+ char *tmpl;
+ char *fn;
+
+ if ((t[0] != '/') && (t[0] != '\\')
+#if WINDOWS
+ && !(isalpha ((int) t[0]) && (t[0] != '\0') && (t[1] == ':'))
+#endif
+ )
+ {
+ /* FIXME: This uses system codepage on W32, not UTF-8 */
+ tmpdir = getenv ("TMPDIR");
+ tmpdir = tmpdir ? tmpdir : "/tmp";
+ GNUNET_asprintf (&tmpl, "%s/%s%s", tmpdir, t, "XXXXXX");
+ }
+ else
+ {
+ GNUNET_asprintf (&tmpl, "%s%s", t, "XXXXXX");
+ }
+#ifdef MINGW
+ fn = (char *) GNUNET_malloc (MAX_PATH + 1);
+ if (ERROR_SUCCESS != plibc_conv_to_win_path (tmpl, fn))
+ {
+ GNUNET_free (fn);
+ GNUNET_free (tmpl);
+ return NULL;
+ }
+ GNUNET_free (tmpl);
+#else
+ fn = tmpl;
+#endif
+ /* FIXME: why is this not MKSTEMP()? This function is implemented in plibc.
+ * CG: really? If I put MKSTEMP here, I get a compilation error...
+ * It will assume that fn is UTF-8-encoded, if compiled with UTF-8 support.
+ */
+ fd = mkstemp (fn);
+ if (fd == -1)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "mkstemp", fn);
+ GNUNET_free (fn);
+ return NULL;
+ }
+ if (0 != CLOSE (fd))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "close", fn);
+ return fn;
+}
+
+
+/**
+ * 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))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
+ return -1;
+ }
+ return buf.f_bavail;
+#elif MINGW
+ DWORD dwDummy;
+ DWORD dwBlocks;
+ wchar_t szDrive[4];
+ wchar_t wpath[MAX_PATH + 1];
+ char *path;
+
+ path = GNUNET_STRINGS_filename_expand (part);
+ if (path == NULL)
+ return -1;
+ /* "part" was in UTF-8, and so is "path" */
+ if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath))
+ {
+ GNUNET_free (path);
+ return -1;
+ }
+ GNUNET_free (path);
+ wcsncpy (szDrive, wpath, 3);
+ szDrive[3] = 0;
+ if (!GetDiskFreeSpaceW (szDrive, &dwDummy, &dwDummy, &dwBlocks, &dwDummy))
+ {
+ 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))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "statfs", part);
+ return -1;
+ }
+ return s.f_bavail;
+#endif
+}
+
+
+/**
+ * Test if "fil" is a directory.
+ * Will not print an error message if the directory
+ * does not exist. Will log errors if GNUNET_SYSERR is
+ * returned (i.e., a file exists with the same name).
+ *
+ * @param fil filename to test
+ * @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)
+ {
+ 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)
+ {
+ 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).
+ *
+ * @param fil filename to check
+ * @return 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)
+ {
+ 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)
+ {
+ 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
+ wchar_t wrdir[MAX_PATH + 1];
+ if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(rdir, wrdir))
+ ret = !CreateDirectoryW (wrdir, NULL);
+ else
+ ret = 1;
+#endif
+ if ((ret != 0) && (errno != EEXIST))
+ {
+ 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 *filename)
+{
+ char *rdir;
+ int len;
+ int ret;
+
+ rdir = GNUNET_STRINGS_filename_expand (filename);
+ 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 h handle to an open file
+ * @param result the buffer to write the result to
+ * @param len the maximum number of bytes to read
+ * @return the number of bytes read on success, GNUNET_SYSERR on failure
+ */
+ssize_t
+GNUNET_DISK_file_read (const struct GNUNET_DISK_FileHandle * h, void *result,
+ size_t len)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ DWORD bytesRead;
+
+ if (h->type != GNUNET_PIPE)
+ {
+ if (!ReadFile (h->h, result, len, &bytesRead, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to read\n");
+#endif
+ if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
+ {
+ if (GetLastError () != ERROR_IO_PENDING)
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
+#endif
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
+#endif
+ GetOverlappedResult (h->h, h->oOverlapRead, &bytesRead, TRUE);
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
+#endif
+ }
+ return bytesRead;
+#else
+ return read (h->fd, result, len);
+#endif
+}
+
+
+/**
+ * Read the contents of a binary file into a buffer.
+ * Guarantees not to block (returns GNUNET_SYSERR and sets errno to EAGAIN
+ * when no data can be read).
+ *
+ * @param h handle to an open file
+ * @param result the buffer to write the result to
+ * @param len the maximum number of bytes to read
+ * @return the number of bytes read on success, GNUNET_SYSERR on failure
+ */
+ssize_t
+GNUNET_DISK_file_read_non_blocking (const struct GNUNET_DISK_FileHandle * h,
+ void *result, size_t len)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ DWORD bytesRead;
+
+ if (h->type != GNUNET_PIPE)
+ {
+ if (!ReadFile (h->h, result, len, &bytesRead, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe, trying to read\n");
+#endif
+ if (!ReadFile (h->h, result, len, &bytesRead, h->oOverlapRead))
+ {
+ if (GetLastError () != ERROR_IO_PENDING)
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Error reading from pipe: %u\n", GetLastError ());
+#endif
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+ else
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "ReadFile() queued a read, cancelling\n");
+#endif
+ CancelIo (h->h);
+ errno = EAGAIN;
+ return GNUNET_SYSERR;
+ }
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Read %u bytes\n", bytesRead);
+#endif
+ }
+ return bytesRead;
+#else
+ int flags;
+ ssize_t ret;
+
+ /* set to non-blocking, read, then set back */
+ flags = fcntl (h->fd, F_GETFL);
+ if (0 == (flags & O_NONBLOCK))
+ fcntl (h->fd, F_SETFL, flags | O_NONBLOCK);
+ ret = read (h->fd, result, len);
+ if (0 == (flags & O_NONBLOCK))
+ fcntl (h->fd, F_SETFL, flags);
+ return ret;
+#endif
+}
+
+
+/**
+ * Read the contents of a binary file into a buffer.
+ *
+ * @param fn file name
+ * @param result the buffer to write the result to
+ * @param len the maximum number of bytes to read
+ * @return number of bytes read, GNUNET_SYSERR on failure
+ */
+ssize_t
+GNUNET_DISK_fn_read (const char *fn, void *result, size_t len)
+{
+ struct GNUNET_DISK_FileHandle *fh;
+ ssize_t ret;
+
+ fh = GNUNET_DISK_file_open (fn, GNUNET_DISK_OPEN_READ, GNUNET_DISK_PERM_NONE);
+ if (!fh)
+ return GNUNET_SYSERR;
+ ret = GNUNET_DISK_file_read (fh, result, len);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
+
+ return ret;
+}
+
+
+/**
+ * Write a buffer to a file.
+ * @param h handle to open file
+ * @param buffer the data to write
+ * @param n number of bytes to write
+ * @return number of bytes written on success, GNUNET_SYSERR on error
+ */
+ssize_t
+GNUNET_DISK_file_write (const struct GNUNET_DISK_FileHandle * h,
+ const void *buffer, size_t n)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ DWORD bytesWritten;
+
+ if (h->type != GNUNET_PIPE)
+ {
+ if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "It is a pipe trying to write %u bytes\n", n);
+#endif
+ if (!WriteFile (h->h, buffer, n, &bytesWritten, h->oOverlapWrite))
+ {
+ if (GetLastError () != ERROR_IO_PENDING)
+ {
+ SetErrnoFromWinError (GetLastError ());
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
+ GetLastError ());
+#endif
+ return GNUNET_SYSERR;
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Will get overlapped result\n");
+#endif
+ if (!GetOverlappedResult (h->h, h->oOverlapWrite, &bytesWritten, TRUE))
+ {
+ SetErrnoFromWinError (GetLastError ());
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Error getting overlapped result while writing to pipe: %u\n",
+ GetLastError ());
+#endif
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ DWORD ovr;
+ if (!GetOverlappedResult (h->h, h->oOverlapWrite, &ovr, TRUE))
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Error getting control overlapped result while writing to pipe: %u\n",
+ GetLastError ());
+#endif
+ }
+ else
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Wrote %u bytes (ovr says %u), picking the greatest\n",
+ bytesWritten, ovr);
+#endif
+ }
+ }
+ if (bytesWritten == 0)
+ {
+ if (n > 0)
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes, returning -1 with EAGAIN\n", bytesWritten);
+#endif
+ errno = EAGAIN;
+ return GNUNET_SYSERR;
+ }
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
+#endif
+ }
+ return bytesWritten;
+#else
+ return write (h->fd, buffer, n);
+#endif
+}
+
+
+/**
+ * Write a buffer to a file, blocking, if necessary.
+ * @param h handle to open file
+ * @param buffer the data to write
+ * @param n number of bytes to write
+ * @return number of bytes written on success, GNUNET_SYSERR on error
+ */
+ssize_t
+GNUNET_DISK_file_write_blocking (const struct GNUNET_DISK_FileHandle * h,
+ const void *buffer, size_t n)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ DWORD bytesWritten;
+ /* We do a non-overlapped write, which is as blocking as it gets */
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Writing %u bytes\n", n);
+#endif
+ if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
+ GetLastError ());
+#endif
+ return GNUNET_SYSERR;
+ }
+ if (bytesWritten == 0 && n > 0)
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Waiting for pipe to clean\n");
+#endif
+ WaitForSingleObject (h->h, INFINITE);
+ if (!WriteFile (h->h, buffer, n, &bytesWritten, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Error writing to pipe: %u\n",
+ GetLastError ());
+#endif
+ return GNUNET_SYSERR;
+ }
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Wrote %u bytes\n", bytesWritten);
+#endif
+ return bytesWritten;
+#else
+ int flags;
+ ssize_t ret;
+
+ /* set to blocking, write, then set back */
+ flags = fcntl (h->fd, F_GETFL);
+ if (0 != (flags & O_NONBLOCK))
+ fcntl (h->fd, F_SETFL, flags - O_NONBLOCK);
+ ret = write (h->fd, buffer, n);
+ if (0 == (flags & O_NONBLOCK))
+ fcntl (h->fd, F_SETFL, flags);
+ return ret;
+#endif
+}
+
+
+/**
+ * Write a buffer to a file. If the file is longer than the
+ * number of bytes that will be written, it will be truncated.
+ *
+ * @param fn file name
+ * @param buffer the data to write
+ * @param n number of bytes to write
+ * @param mode file permissions
+ * @return number of bytes written on success, GNUNET_SYSERR on error
+ */
+ssize_t
+GNUNET_DISK_fn_write (const char *fn, const void *buffer, size_t n,
+ enum GNUNET_DISK_AccessPermissions mode)
+{
+ struct GNUNET_DISK_FileHandle *fh;
+ ssize_t ret;
+
+ fh = GNUNET_DISK_file_open (fn,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_TRUNCATE
+ | GNUNET_DISK_OPEN_CREATE, mode);
+ if (!fh)
+ return GNUNET_SYSERR;
+ ret = GNUNET_DISK_file_write (fh, buffer, n);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_file_close (fh));
+ return ret;
+}
+
+
+/**
+ * Scan a directory for files.
+ *
+ * @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 callback_cls closure for 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 *callback_cls)
+{
+ 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);
+ if (dname == NULL)
+ return GNUNET_SYSERR;
+ while ((strlen (dname) > 0) && (dname[strlen (dname) - 1] == DIR_SEPARATOR))
+ dname[strlen (dname) - 1] = '\0';
+ if (0 != STAT (dname, &istat))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "stat", dname);
+ GNUNET_free (dname);
+ return GNUNET_SYSERR;
+ }
+ if (!S_ISDIR (istat.st_mode))
+ {
+ 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))
+ {
+ 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 (callback_cls, 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
+{
+
+ /**
+ * 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_with_priority (iter->priority, &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 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
+ * @return GNUNET_YES if directory is not empty and 'callback'
+ * will be called later, GNUNET_NO otherwise, GNUNET_SYSERR on error.
+ */
+int
+GNUNET_DISK_directory_iterator_start (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->callback = callback;
+ di->callback_cls = callback_cls;
+ di->directory = OPENDIR (dirName);
+ if (di->directory == NULL)
+ {
+ GNUNET_free (di);
+ callback (callback_cls, NULL, NULL, NULL);
+ return GNUNET_SYSERR;
+ }
+ di->dirname = GNUNET_strdup (dirName);
+ di->priority = prio;
+ return GNUNET_DISK_directory_iterator_next (di, GNUNET_NO);
+}
+
+
+/**
+ * Function that removes the given directory by calling
+ * "GNUNET_DISK_directory_remove".
+ *
+ * @param unused not used
+ * @param fn directory to remove
+ * @return GNUNET_OK
+ */
+static int
+remove_helper (void *unused, const char *fn)
+{
+ (void) 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... */
+ CHMOD (fileName, S_IWUSR | S_IRUSR | S_IXUSR);
+ 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)))
+ {
+ 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))
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "rmdir", fileName);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Copy a file.
+ *
+ * @param src file to copy
+ * @param dst destination file name
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_DISK_file_copy (const char *src, const char *dst)
+{
+ char *buf;
+ uint64_t pos;
+ uint64_t size;
+ size_t len;
+ struct GNUNET_DISK_FileHandle *in;
+ struct GNUNET_DISK_FileHandle *out;
+
+ if (GNUNET_OK != GNUNET_DISK_file_size (src, &size, GNUNET_YES))
+ return GNUNET_SYSERR;
+ pos = 0;
+ in = GNUNET_DISK_file_open (src, GNUNET_DISK_OPEN_READ,
+ GNUNET_DISK_PERM_NONE);
+ if (!in)
+ return GNUNET_SYSERR;
+ out =
+ GNUNET_DISK_file_open (dst,
+ GNUNET_DISK_OPEN_WRITE | GNUNET_DISK_OPEN_CREATE |
+ GNUNET_DISK_OPEN_FAILIFEXISTS,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE |
+ GNUNET_DISK_PERM_GROUP_READ |
+ GNUNET_DISK_PERM_GROUP_WRITE);
+ if (!out)
+ {
+ GNUNET_DISK_file_close (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 != GNUNET_DISK_file_read (in, buf, len))
+ goto FAIL;
+ if (len != GNUNET_DISK_file_write (out, buf, len))
+ goto FAIL;
+ pos += len;
+ }
+ GNUNET_free (buf);
+ GNUNET_DISK_file_close (in);
+ GNUNET_DISK_file_close (out);
+ return GNUNET_OK;
+FAIL:
+ GNUNET_free (buf);
+ GNUNET_DISK_file_close (in);
+ GNUNET_DISK_file_close (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
+ *
+ * @param filename name of file to change the owner of
+ * @param user name of the new owner
+ * @return GNUNET_OK on success, GNUNET_SYSERR on failure
+ */
+int
+GNUNET_DISK_file_change_owner (const char *filename, const char *user)
+{
+#ifndef MINGW
+ struct passwd *pws;
+
+ pws = getpwnam (user);
+ if (pws == NULL)
+ {
+ 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))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "chown", filename);
+#endif
+ return GNUNET_OK;
+}
+
+
+/**
+ * Lock a part of a file
+ * @param fh file handle
+ * @param lockStart absolute position from where to lock
+ * @param lockEnd absolute position until where to lock
+ * @param excl GNUNET_YES for an exclusive lock
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_DISK_file_lock (struct GNUNET_DISK_FileHandle *fh, OFF_T lockStart,
+ OFF_T lockEnd, int excl)
+{
+ if (fh == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifndef MINGW
+ struct flock fl;
+
+ memset (&fl, 0, sizeof (struct flock));
+ fl.l_type = excl ? F_WRLCK : F_RDLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = lockStart;
+ fl.l_len = lockEnd;
+
+ return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
+#else
+ OVERLAPPED o;
+ OFF_T diff = lockEnd - lockStart;
+ DWORD diff_low, diff_high;
+ diff_low = (DWORD) (diff & 0xFFFFFFFF);
+ diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
+
+ memset (&o, 0, sizeof (OVERLAPPED));
+ o.Offset = (DWORD) (lockStart & 0xFFFFFFFF);;
+ o.OffsetHigh = (DWORD) (((lockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
+
+ if (!LockFileEx
+ (fh->h, (excl ? LOCKFILE_EXCLUSIVE_LOCK : 0) | LOCKFILE_FAIL_IMMEDIATELY,
+ 0, diff_low, diff_high, &o))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+#endif
+}
+
+
+/**
+ * Unlock a part of a file
+ * @param fh file handle
+ * @param unlockStart absolute position from where to unlock
+ * @param unlockEnd absolute position until where to unlock
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_DISK_file_unlock (struct GNUNET_DISK_FileHandle *fh, OFF_T unlockStart,
+ OFF_T unlockEnd)
+{
+ if (fh == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifndef MINGW
+ struct flock fl;
+
+ memset (&fl, 0, sizeof (struct flock));
+ fl.l_type = F_UNLCK;
+ fl.l_whence = SEEK_SET;
+ fl.l_start = unlockStart;
+ fl.l_len = unlockEnd;
+
+ return fcntl (fh->fd, F_SETLK, &fl) != 0 ? GNUNET_SYSERR : GNUNET_OK;
+#else
+ OVERLAPPED o;
+ OFF_T diff = unlockEnd - unlockStart;
+ DWORD diff_low, diff_high;
+ diff_low = (DWORD) (diff & 0xFFFFFFFF);
+ diff_high = (DWORD) ((diff >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
+
+ memset (&o, 0, sizeof (OVERLAPPED));
+ o.Offset = (DWORD) (unlockStart & 0xFFFFFFFF);;
+ o.OffsetHigh = (DWORD) (((unlockStart & ~0xFFFFFFFF) >> (sizeof (DWORD) * 8)) & 0xFFFFFFFF);
+
+ if (!UnlockFileEx (fh->h, 0, diff_low, diff_high, &o))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return GNUNET_SYSERR;
+ }
+
+ return GNUNET_OK;
+#endif
+}
+
+
+/**
+ * Open a file. Note that the access permissions will only be
+ * used if a new file is created and if the underlying operating
+ * system supports the given permissions.
+ *
+ * @param fn file name to be opened
+ * @param flags opening flags, a combination of GNUNET_DISK_OPEN_xxx bit flags
+ * @param perm permissions for the newly created file, use
+ * GNUNET_DISK_PERM_USER_NONE if a file could not be created by this
+ * call (because of flags)
+ * @return IO handle on success, NULL on error
+ */
+struct GNUNET_DISK_FileHandle *
+GNUNET_DISK_file_open (const char *fn, enum GNUNET_DISK_OpenFlags flags,
+ enum GNUNET_DISK_AccessPermissions perm)
+{
+ char *expfn;
+ struct GNUNET_DISK_FileHandle *ret;
+
+#ifdef MINGW
+ DWORD access;
+ DWORD disp;
+ HANDLE h;
+ wchar_t wexpfn[MAX_PATH + 1];
+#else
+ int oflags;
+ int mode;
+ int fd;
+#endif
+
+ expfn = GNUNET_STRINGS_filename_expand (fn);
+ if (NULL == expfn)
+ return NULL;
+#ifndef MINGW
+ mode = 0;
+ if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
+ oflags = O_RDWR; /* note: O_RDWR is NOT always O_RDONLY | O_WRONLY */
+ else if (flags & GNUNET_DISK_OPEN_READ)
+ oflags = O_RDONLY;
+ else if (flags & GNUNET_DISK_OPEN_WRITE)
+ oflags = O_WRONLY;
+ else
+ {
+ GNUNET_break (0);
+ GNUNET_free (expfn);
+ return NULL;
+ }
+ if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
+ oflags |= (O_CREAT | O_EXCL);
+ if (flags & GNUNET_DISK_OPEN_TRUNCATE)
+ oflags |= O_TRUNC;
+ if (flags & GNUNET_DISK_OPEN_APPEND)
+ oflags |= O_APPEND;
+ if (flags & GNUNET_DISK_OPEN_CREATE)
+ {
+ (void) GNUNET_DISK_directory_create_for_file (expfn);
+ oflags |= O_CREAT;
+ mode = translate_unix_perms (perm);
+ }
+
+ fd = open (expfn, oflags | O_LARGEFILE, mode);
+ if (fd == -1)
+ {
+ if (0 == (flags & GNUNET_DISK_OPEN_FAILIFEXISTS))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
+ else
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_DEBUG, "open", expfn);
+ GNUNET_free (expfn);
+ return NULL;
+ }
+#else
+ access = 0;
+ disp = OPEN_ALWAYS;
+
+ if (GNUNET_DISK_OPEN_READWRITE == (flags & GNUNET_DISK_OPEN_READWRITE))
+ access = FILE_READ_DATA | FILE_WRITE_DATA;
+ else if (flags & GNUNET_DISK_OPEN_READ)
+ access = FILE_READ_DATA;
+ else if (flags & GNUNET_DISK_OPEN_WRITE)
+ access = FILE_WRITE_DATA;
+
+ if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
+ {
+ disp = CREATE_NEW;
+ }
+ else if (flags & GNUNET_DISK_OPEN_CREATE)
+ {
+ (void) GNUNET_DISK_directory_create_for_file (expfn);
+ if (flags & GNUNET_DISK_OPEN_TRUNCATE)
+ disp = CREATE_ALWAYS;
+ else
+ disp = OPEN_ALWAYS;
+ }
+ else if (flags & GNUNET_DISK_OPEN_TRUNCATE)
+ {
+ disp = TRUNCATE_EXISTING;
+ }
+ else
+ {
+ disp = OPEN_EXISTING;
+ }
+
+ if (ERROR_SUCCESS == plibc_conv_to_win_pathwconv(expfn, wexpfn))
+ h = CreateFileW (wexpfn, access,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
+ disp, FILE_ATTRIBUTE_NORMAL, NULL);
+ else
+ h = INVALID_HANDLE_VALUE;
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", expfn);
+ GNUNET_free (expfn);
+ return NULL;
+ }
+
+ if (flags & GNUNET_DISK_OPEN_APPEND)
+ if (SetFilePointer (h, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "SetFilePointer", expfn);
+ CloseHandle (h);
+ GNUNET_free (expfn);
+ return NULL;
+ }
+#endif
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
+#ifdef MINGW
+ ret->h = h;
+ ret->type = GNUNET_DISK_FILE;
+#else
+ ret->fd = fd;
+#endif
+ GNUNET_free (expfn);
+ return ret;
+}
+
+
+/**
+ * Close an open file
+ * @param h file handle
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_file_close (struct GNUNET_DISK_FileHandle *h)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#if MINGW
+ if (!CloseHandle (h->h))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
+ GNUNET_free (h->oOverlapRead);
+ GNUNET_free (h->oOverlapWrite);
+ GNUNET_free (h);
+ return GNUNET_SYSERR;
+ }
+#else
+ if (close (h->fd) != 0)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "close");
+ GNUNET_free (h);
+ return GNUNET_SYSERR;
+ }
+#endif
+ GNUNET_free (h);
+ 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 cfg configuration to use (determines HOME)
+ * @param serviceName name of the service
+ * @param ... is NULL-terminated list of
+ * path components to append to the
+ * private directory name.
+ * @return the constructed filename
+ */
+char *
+GNUNET_DISK_get_home_filename (const 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)
+ {
+ 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] != '\\'))
+ (void) GNUNET_DISK_directory_create_for_file (ret);
+ else
+ (void) GNUNET_DISK_directory_create (ret);
+ return ret;
+}
+
+
+/**
+ * Handle for a memory-mapping operation.
+ */
+struct GNUNET_DISK_MapHandle
+{
+ /**
+ * Address where the map is in memory.
+ */
+ void *addr;
+
+#ifdef MINGW
+ /**
+ * Underlying OS handle.
+ */
+ HANDLE h;
+#else
+ /**
+ * Number of bytes mapped.
+ */
+ size_t len;
+#endif
+};
+
+
+#ifndef MAP_FAILED
+#define MAP_FAILED ((void *) -1)
+#endif
+
+/**
+ * Map a file into memory
+ *
+ * @param h open file handle
+ * @param m handle to the new mapping
+ * @param access access specification, GNUNET_DISK_MAP_TYPE_xxx
+ * @param len size of the mapping
+ * @return pointer to the mapped memory region, NULL on failure
+ */
+void *
+GNUNET_DISK_file_map (const struct GNUNET_DISK_FileHandle *h,
+ struct GNUNET_DISK_MapHandle **m,
+ enum GNUNET_DISK_MapType access, size_t len)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return NULL;
+ }
+
+#ifdef MINGW
+ DWORD mapAccess, protect;
+
+ if ((access & GNUNET_DISK_MAP_TYPE_READ) &&
+ (access & GNUNET_DISK_MAP_TYPE_WRITE))
+ {
+ protect = PAGE_READWRITE;
+ mapAccess = FILE_MAP_ALL_ACCESS;
+ }
+ else if (access & GNUNET_DISK_MAP_TYPE_READ)
+ {
+ protect = PAGE_READONLY;
+ mapAccess = FILE_MAP_READ;
+ }
+ else if (access & GNUNET_DISK_MAP_TYPE_WRITE)
+ {
+ protect = PAGE_READWRITE;
+ mapAccess = FILE_MAP_WRITE;
+ }
+ else
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+
+ *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
+ (*m)->h = CreateFileMapping (h->h, NULL, protect, 0, 0, NULL);
+ if ((*m)->h == INVALID_HANDLE_VALUE)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ GNUNET_free (*m);
+ return NULL;
+ }
+
+ (*m)->addr = MapViewOfFile ((*m)->h, mapAccess, 0, 0, len);
+ if (!(*m)->addr)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ CloseHandle ((*m)->h);
+ GNUNET_free (*m);
+ }
+
+ return (*m)->addr;
+#else
+ int prot;
+
+ prot = 0;
+ if (access & GNUNET_DISK_MAP_TYPE_READ)
+ prot = PROT_READ;
+ if (access & GNUNET_DISK_MAP_TYPE_WRITE)
+ prot |= PROT_WRITE;
+ *m = GNUNET_malloc (sizeof (struct GNUNET_DISK_MapHandle));
+ (*m)->addr = mmap (NULL, len, prot, MAP_SHARED, h->fd, 0);
+ GNUNET_assert (NULL != (*m)->addr);
+ if (MAP_FAILED == (*m)->addr)
+ {
+ GNUNET_free (*m);
+ return NULL;
+ }
+ (*m)->len = len;
+ return (*m)->addr;
+#endif
+}
+
+/**
+ * Unmap a file
+ * @param h mapping handle
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_file_unmap (struct GNUNET_DISK_MapHandle *h)
+{
+ int ret;
+
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ ret = UnmapViewOfFile (h->addr) ? GNUNET_OK : GNUNET_SYSERR;
+ if (ret != GNUNET_OK)
+ SetErrnoFromWinError (GetLastError ());
+ if (!CloseHandle (h->h) && (ret == GNUNET_OK))
+ {
+ ret = GNUNET_SYSERR;
+ SetErrnoFromWinError (GetLastError ());
+ }
+#else
+ ret = munmap (h->addr, h->len) != -1 ? GNUNET_OK : GNUNET_SYSERR;
+#endif
+ GNUNET_free (h);
+ return ret;
+}
+
+
+/**
+ * Write file changes to disk
+ * @param h handle to an open file
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_file_sync (const struct GNUNET_DISK_FileHandle *h)
+{
+ if (h == NULL)
+ {
+ errno = EINVAL;
+ return GNUNET_SYSERR;
+ }
+
+#ifdef MINGW
+ int ret;
+
+ ret = FlushFileBuffers (h->h) ? GNUNET_OK : GNUNET_SYSERR;
+ if (ret != GNUNET_OK)
+ SetErrnoFromWinError (GetLastError ());
+ return ret;
+#elif defined(FREEBSD) || defined(OPENBSD) || defined(DARWIN)
+ return fsync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
+#else
+ return fdatasync (h->fd) == -1 ? GNUNET_SYSERR : GNUNET_OK;
+#endif
+}
+
+
+#if WINDOWS
+/* Copyright Bob Byrnes <byrnes <at> curl.com>
+ http://permalink.gmane.org/gmane.os.cygwin.patches/2121
+*/
+/* Create a pipe, and return handles to the read and write ends,
+ just like CreatePipe, but ensure that the write end permits
+ FILE_READ_ATTRIBUTES access, on later versions of win32 where
+ this is supported. This access is needed by NtQueryInformationFile,
+ which is used to implement select and nonblocking writes.
+ Note that the return value is either NO_ERROR or GetLastError,
+ unlike CreatePipe, which returns a bool for success or failure. */
+static int
+create_selectable_pipe (PHANDLE read_pipe_ptr, PHANDLE write_pipe_ptr,
+ LPSECURITY_ATTRIBUTES sa_ptr, DWORD psize,
+ DWORD dwReadMode, DWORD dwWriteMode)
+{
+ /* Default to error. */
+ *read_pipe_ptr = *write_pipe_ptr = INVALID_HANDLE_VALUE;
+
+ HANDLE read_pipe = INVALID_HANDLE_VALUE, write_pipe = INVALID_HANDLE_VALUE;
+
+ /* Ensure that there is enough pipe buffer space for atomic writes. */
+ if (psize < PIPE_BUF)
+ psize = PIPE_BUF;
+
+ char pipename[MAX_PATH];
+
+ /* Retry CreateNamedPipe as long as the pipe name is in use.
+ * Retrying will probably never be necessary, but we want
+ * to be as robust as possible. */
+ while (1)
+ {
+ static volatile LONG pipe_unique_id;
+
+ snprintf (pipename, sizeof pipename, "\\\\.\\pipe\\gnunet-%d-%ld",
+ getpid (), InterlockedIncrement ((LONG *) & pipe_unique_id));
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateNamedPipe: name = %s, size = %lu\n",
+ pipename, psize);
+#endif
+ /* Use CreateNamedPipe instead of CreatePipe, because the latter
+ * returns a write handle that does not permit FILE_READ_ATTRIBUTES
+ * access, on versions of win32 earlier than WinXP SP2.
+ * CreatePipe also stupidly creates a full duplex pipe, which is
+ * a waste, since only a single direction is actually used.
+ * It's important to only allow a single instance, to ensure that
+ * the pipe was not created earlier by some other process, even if
+ * the pid has been reused. We avoid FILE_FLAG_FIRST_PIPE_INSTANCE
+ * because that is only available for Win2k SP2 and WinXP. */
+ read_pipe = CreateNamedPipeA (pipename, PIPE_ACCESS_INBOUND | dwReadMode, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 1, /* max instances */
+ psize, /* output buffer size */
+ psize, /* input buffer size */
+ NMPWAIT_USE_DEFAULT_WAIT, sa_ptr);
+
+ if (read_pipe != INVALID_HANDLE_VALUE)
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n", read_pipe);
+#endif
+ break;
+ }
+
+ DWORD err = GetLastError ();
+
+ switch (err)
+ {
+ case ERROR_PIPE_BUSY:
+ /* The pipe is already open with compatible parameters.
+ * Pick a new name and retry. */
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe busy, retrying\n");
+#endif
+ continue;
+ case ERROR_ACCESS_DENIED:
+ /* The pipe is already open with incompatible parameters.
+ * Pick a new name and retry. */
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe access denied, retrying\n");
+#endif
+ continue;
+ case ERROR_CALL_NOT_IMPLEMENTED:
+ /* We are on an older Win9x platform without named pipes.
+ * Return an anonymous pipe as the best approximation. */
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "CreateNamedPipe not implemented, resorting to "
+ "CreatePipe: size = %lu\n", psize);
+#endif
+ if (CreatePipe (read_pipe_ptr, write_pipe_ptr, sa_ptr, psize))
+ {
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe read handle = %p\n",
+ *read_pipe_ptr);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n",
+ *write_pipe_ptr);
+#endif
+ return GNUNET_OK;
+ }
+ err = GetLastError ();
+ LOG (GNUNET_ERROR_TYPE_ERROR, "CreatePipe failed: %d\n", err);
+ return err;
+ default:
+ LOG (GNUNET_ERROR_TYPE_ERROR, "CreateNamedPipe failed: %d\n", err);
+ return err;
+ }
+ /* NOTREACHED */
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile: name = %s\n", pipename);
+#endif
+
+ /* Open the named pipe for writing.
+ * Be sure to permit FILE_READ_ATTRIBUTES access. */
+ write_pipe = CreateFileA (pipename, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0, /* share mode */
+ sa_ptr, OPEN_EXISTING, dwWriteMode, /* flags and attributes */
+ 0); /* handle to template file */
+
+ if (write_pipe == INVALID_HANDLE_VALUE)
+ {
+ /* Failure. */
+ DWORD err = GetLastError ();
+
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "CreateFile failed: %d\n", err);
+#endif
+ CloseHandle (read_pipe);
+ return err;
+ }
+#if DEBUG_PIPE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "pipe write handle = %p\n", write_pipe);
+#endif
+ /* Success. */
+ *read_pipe_ptr = read_pipe;
+ *write_pipe_ptr = write_pipe;
+ return GNUNET_OK;
+}
+#endif
+
+
+/**
+ * Creates an interprocess channel
+ *
+ * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
+ * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
+ * @param inherit_read inherit the parent processes stdin (only for windows)
+ * @param inherit_write inherit the parent processes stdout (only for windows)
+ * @return handle to the new pipe, NULL on error
+ */
+struct GNUNET_DISK_PipeHandle *
+GNUNET_DISK_pipe (int blocking_read, int blocking_write, int inherit_read, int inherit_write)
+{
+#ifndef MINGW
+ int fd[2];
+ int ret;
+ int eno;
+
+ ret = pipe (fd);
+ if (ret == -1)
+ {
+ eno = errno;
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
+ errno = eno;
+ return NULL;
+ }
+ return GNUNET_DISK_pipe_from_fd (blocking_read,
+ blocking_write,
+ fd);
+#else
+ struct GNUNET_DISK_PipeHandle *p;
+ struct GNUNET_DISK_FileHandle *fds;
+ BOOL ret;
+ HANDLE tmp_handle;
+
+
+ p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
+ 2 * sizeof (struct GNUNET_DISK_FileHandle));
+ fds = (struct GNUNET_DISK_FileHandle *) &p[1];
+ p->fd[0] = &fds[0];
+ p->fd[1] = &fds[1];
+
+ /* All pipes are overlapped. If you want them to block - just
+ * call WriteFile() and ReadFile() with NULL overlapped pointer.
+ */
+ ret =
+ create_selectable_pipe (&p->fd[0]->h, &p->fd[1]->h, NULL, 0,
+ FILE_FLAG_OVERLAPPED,
+ FILE_FLAG_OVERLAPPED);
+ if (!ret)
+ {
+ GNUNET_free (p);
+ SetErrnoFromWinError (GetLastError ());
+ return NULL;
+ }
+ if (!DuplicateHandle
+ (GetCurrentProcess (), p->fd[0]->h, GetCurrentProcess (), &tmp_handle, 0,
+ inherit_read == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ CloseHandle (p->fd[0]->h);
+ CloseHandle (p->fd[1]->h);
+ GNUNET_free (p);
+ return NULL;
+ }
+ CloseHandle (p->fd[0]->h);
+ p->fd[0]->h = tmp_handle;
+
+ if (!DuplicateHandle
+ (GetCurrentProcess (), p->fd[1]->h, GetCurrentProcess (), &tmp_handle, 0,
+ inherit_write == GNUNET_YES ? TRUE : FALSE, DUPLICATE_SAME_ACCESS))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ CloseHandle (p->fd[0]->h);
+ CloseHandle (p->fd[1]->h);
+ GNUNET_free (p);
+ return NULL;
+ }
+ CloseHandle (p->fd[1]->h);
+ p->fd[1]->h = tmp_handle;
+
+ p->fd[0]->type = GNUNET_PIPE;
+ p->fd[1]->type = GNUNET_PIPE;
+
+ p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
+
+ p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+ p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+ return p;
+#endif
+}
+
+
+/**
+ * Creates a pipe object from a couple of file descriptors.
+ * Useful for wrapping existing pipe FDs.
+ *
+ * @param blocking_read creates an asynchronous pipe for reading if set to GNUNET_NO
+ * @param blocking_write creates an asynchronous pipe for writing if set to GNUNET_NO
+ * @param fd an array of two fd values. One of them may be -1 for read-only or write-only pipes
+ *
+ * @return handle to the new pipe, NULL on error
+ */
+struct GNUNET_DISK_PipeHandle *
+GNUNET_DISK_pipe_from_fd (int blocking_read, int blocking_write, int fd[2])
+{
+ struct GNUNET_DISK_PipeHandle *p;
+ struct GNUNET_DISK_FileHandle *fds;
+
+ p = GNUNET_malloc (sizeof (struct GNUNET_DISK_PipeHandle) +
+ 2 * sizeof (struct GNUNET_DISK_FileHandle));
+ fds = (struct GNUNET_DISK_FileHandle *) &p[1];
+ p->fd[0] = &fds[0];
+ p->fd[1] = &fds[1];
+#ifndef MINGW
+ int ret;
+ int flags;
+ int eno = 0; /* make gcc happy */
+
+ p->fd[0]->fd = fd[0];
+ p->fd[1]->fd = fd[1];
+ ret = 0;
+ if (fd[0] >= 0)
+ {
+ if (!blocking_read)
+ {
+ flags = fcntl (fd[0], F_GETFL);
+ flags |= O_NONBLOCK;
+ if (0 > fcntl (fd[0], F_SETFL, flags))
+ {
+ ret = -1;
+ eno = errno;
+ }
+ }
+ flags = fcntl (fd[0], F_GETFD);
+ flags |= FD_CLOEXEC;
+ if (0 > fcntl (fd[0], F_SETFD, flags))
+ {
+ ret = -1;
+ eno = errno;
+ }
+ }
+
+ if (fd[1] >= 0)
+ {
+ if (!blocking_write)
+ {
+ flags = fcntl (fd[1], F_GETFL);
+ flags |= O_NONBLOCK;
+ if (0 > fcntl (fd[1], F_SETFL, flags))
+ {
+ ret = -1;
+ eno = errno;
+ }
+ }
+ flags = fcntl (fd[1], F_GETFD);
+ flags |= FD_CLOEXEC;
+ if (0 > fcntl (fd[1], F_SETFD, flags))
+ {
+ ret = -1;
+ eno = errno;
+ }
+ }
+ if (ret == -1)
+ {
+ errno = eno;
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fcntl");
+ if (p->fd[0]->fd >= 0)
+ GNUNET_break (0 == close (p->fd[0]->fd));
+ if (p->fd[1]->fd >= 0)
+ GNUNET_break (0 == close (p->fd[1]->fd));
+ GNUNET_free (p);
+ errno = eno;
+ return NULL;
+ }
+#else
+ if (fd[0] >= 0)
+ p->fd[0]->h = _get_osfhandle (fd[0]);
+ else
+ p->fd[0]->h = INVALID_HANDLE_VALUE;
+ if (fd[1] >= 0)
+ p->fd[1]->h = _get_osfhandle (fd[1]);
+ else
+ p->fd[1]->h = INVALID_HANDLE_VALUE;
+
+ if (p->fd[0]->h != INVALID_HANDLE_VALUE)
+ {
+ p->fd[0]->type = GNUNET_PIPE;
+ p->fd[0]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[0]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[0]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ p->fd[0]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ }
+
+ if (p->fd[1]->h != INVALID_HANDLE_VALUE)
+ {
+ p->fd[1]->type = GNUNET_PIPE;
+ p->fd[1]->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[1]->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
+ p->fd[1]->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ p->fd[1]->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ }
+#endif
+ return p;
+}
+
+
+/**
+ * Closes an interprocess channel
+ *
+ * @param p pipe to close
+ * @param end which end of the pipe to close
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_pipe_close_end (struct GNUNET_DISK_PipeHandle *p,
+ enum GNUNET_DISK_PipeEnd end)
+{
+ int ret = GNUNET_OK;
+ int save;
+
+#ifdef MINGW
+ if (end == GNUNET_DISK_PIPE_END_READ)
+ {
+ if (p->fd[0]->h != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle (p->fd[0]->h))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ ret = GNUNET_SYSERR;
+ }
+ GNUNET_free (p->fd[0]->oOverlapRead);
+ GNUNET_free (p->fd[0]->oOverlapWrite);
+ p->fd[0]->h = INVALID_HANDLE_VALUE;
+ }
+ }
+ else if (end == GNUNET_DISK_PIPE_END_WRITE)
+ {
+ if (p->fd[0]->h != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle (p->fd[1]->h))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ ret = GNUNET_SYSERR;
+ }
+ GNUNET_free (p->fd[1]->oOverlapRead);
+ GNUNET_free (p->fd[1]->oOverlapWrite);
+ p->fd[1]->h = INVALID_HANDLE_VALUE;
+ }
+ }
+ save = errno;
+#else
+ save = 0;
+ if (end == GNUNET_DISK_PIPE_END_READ)
+ {
+ if (0 != close (p->fd[0]->fd))
+ {
+ ret = GNUNET_SYSERR;
+ save = errno;
+ }
+ p->fd[0]->fd = -1;
+ }
+ else if (end == GNUNET_DISK_PIPE_END_WRITE)
+ {
+ if (0 != close (p->fd[1]->fd))
+ {
+ ret = GNUNET_SYSERR;
+ save = errno;
+ }
+ p->fd[1]->fd = -1;
+ }
+#endif
+ errno = save;
+ return ret;
+}
+
+
+/**
+ * Closes an interprocess channel
+ *
+ * @param p pipe to close
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_pipe_close (struct GNUNET_DISK_PipeHandle *p)
+{
+ int ret = GNUNET_OK;
+ int save;
+
+#ifdef MINGW
+ if (p->fd[0]->h != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle (p->fd[0]->h))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ ret = GNUNET_SYSERR;
+ }
+ GNUNET_free (p->fd[0]->oOverlapRead);
+ GNUNET_free (p->fd[0]->oOverlapWrite);
+ }
+ if (p->fd[1]->h != INVALID_HANDLE_VALUE)
+ {
+ if (!CloseHandle (p->fd[1]->h))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ ret = GNUNET_SYSERR;
+ }
+ GNUNET_free (p->fd[1]->oOverlapRead);
+ GNUNET_free (p->fd[1]->oOverlapWrite);
+ }
+ save = errno;
+#else
+ save = 0;
+ if (p->fd[0]->fd != -1)
+ {
+ if (0 != close (p->fd[0]->fd))
+ {
+ ret = GNUNET_SYSERR;
+ save = errno;
+ }
+ }
+
+ if (p->fd[1]->fd != -1)
+ {
+ if (0 != close (p->fd[1]->fd))
+ {
+ ret = GNUNET_SYSERR;
+ save = errno;
+ }
+ }
+#endif
+ GNUNET_free (p);
+ errno = save;
+ return ret;
+}
+
+
+/**
+ * Get the handle to a particular pipe end
+ *
+ * @param p pipe
+ * @param n end to access
+ * @return handle for the respective end
+ */
+const struct GNUNET_DISK_FileHandle *
+GNUNET_DISK_pipe_handle (const struct GNUNET_DISK_PipeHandle *p,
+ enum GNUNET_DISK_PipeEnd n)
+{
+ switch (n)
+ {
+ case GNUNET_DISK_PIPE_END_READ:
+ case GNUNET_DISK_PIPE_END_WRITE:
+ return p->fd[n];
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+}
+
+
+/**
+ * Retrieve OS file handle
+ * @internal
+ * @param fh GNUnet file descriptor
+ * @param dst destination buffer
+ * @param dst_len length of dst
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
+ void *dst, size_t dst_len)
+{
+#ifdef MINGW
+ if (dst_len < sizeof (HANDLE))
+ return GNUNET_SYSERR;
+ *((HANDLE *) dst) = fh->h;
+#else
+ if (dst_len < sizeof (int))
+ return GNUNET_SYSERR;
+ *((int *) dst) = fh->fd;
+#endif
+
+ return GNUNET_OK;
+}
+
+/* end of disk.c */
diff --git a/src/util/disk.h b/src/util/disk.h
new file mode 100644
index 0000000..2c12874
--- /dev/null
+++ b/src/util/disk.h
@@ -0,0 +1,44 @@
+/*
+ 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/disk.h
+ * @brief Internal DISK related helper functions
+ * @author Nils Durner
+ */
+#ifndef GNUNET_DISK_H_
+#define GNUNET_DISK_H_
+
+#include "gnunet_disk_lib.h"
+
+/**
+ * Retrieve OS file handle
+ *
+ * @internal
+ * @param fh GNUnet file descriptor
+ * @param dst destination buffer
+ * @param dst_len length of dst
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_DISK_internal_file_handle_ (const struct GNUNET_DISK_FileHandle *fh,
+ void *dst, size_t dst_len);
+
+#endif /* GNUNET_DISK_H_ */
diff --git a/src/util/getopt.c b/src/util/getopt.c
new file mode 100644
index 0000000..1699498
--- /dev/null
+++ b/src/util/getopt.c
@@ -0,0 +1,1050 @@
+/* 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 <unixlib.h>
+#if HAVE_STRING_H - 0
+#include <string.h>
+#endif
+#endif
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#if defined (WIN32) && !defined (__CYGWIN32__)
+/* It's not Unix, really. See? Capital letters. */
+#include <windows.h>
+#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 <libintl.h>
+#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;
+
+/* 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;
+
+
+/* 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 <string.h>
+#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 GNUNET_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 (int argc, char *const *argv, const char *optstring,
+ const struct GNoption *longopts, int *longind,
+ int long_only)
+{
+ static int __getopt_initialized = 0;
+ static int GNopterr = 1;
+
+ 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++;
+ 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);
+ 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;
+ }
+
+ /* 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++;
+ 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);
+ }
+ 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);
+ }
+ 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. */
+ if (longopts != NULL)
+ 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);
+ }
+ 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 binaryOptions Name of application with option summary
+ * @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,
+ 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;
+ 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 %s to get a list of options.\n"), "--help");
+ 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 0000000..8fb3673
--- /dev/null
+++ b/src/util/getopt_helpers.c
@@ -0,0 +1,290 @@
+/*
+ This file is part of GNUnet
+ (C) 2006, 2011 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"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+
+/**
+ * Print out program version (implements --version).
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (points to version string)
+ * @param option name of the option
+ * @param value not used (NULL)
+ * @return GNUNET_SYSERR (do not continue)
+ */
+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
+
+/**
+ * Print out details on command line options (implements --help).
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (points to about text)
+ * @param option name of the option
+ * @param value not used (NULL)
+ * @return GNUNET_SYSERR (do not continue)
+ */
+int
+GNUNET_GETOPT_format_help_ (struct GNUNET_GETOPT_CommandLineProcessorContext
+ *ctx, void *scls, const char *option,
+ const char *value)
+{
+ const char *about = scls;
+ size_t slen;
+ unsigned int i;
+ int j;
+ size_t ml;
+ size_t 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"));
+ 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", (int) (BORDER - slen), "");
+ slen = BORDER;
+ }
+ if (0 < strlen (opt[i].description))
+ trans = gettext (opt[i].description);
+ else
+ trans = "";
+ ml = strlen (trans);
+ p = 0;
+OUTER:
+ while (ml - p > 78 - slen)
+ {
+ for (j = p + 78 - slen; j > p; j--)
+ {
+ if (isspace ((unsigned char) 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;
+}
+
+
+/**
+ * Set an option of type 'unsigned int' from the command line. Each
+ * time the option flag is given, the value is incremented by one.
+ * A pointer to this function should be passed as part of the
+ * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
+ * of this type. It should be followed by a pointer to a value of
+ * type 'int'.
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (will point to the 'int')
+ * @param option name of the option
+ * @param value not used (NULL)
+ * @return GNUNET_OK
+ */
+int
+GNUNET_GETOPT_increment_value (struct GNUNET_GETOPT_CommandLineProcessorContext
+ *ctx, void *scls, const char *option,
+ const char *value)
+{
+ int *val = scls;
+
+ (*val)++;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Set an option of type 'int' from the command line to 1 if the
+ * given option is present.
+ * A pointer to this function should be passed as part of the
+ * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
+ * of this type. It should be followed by a pointer to a value of
+ * type 'int'.
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (will point to the 'int')
+ * @param option name of the option
+ * @param value not used (NULL)
+ * @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;
+}
+
+
+/**
+ * Set an option of type 'char *' from the command line.
+ * A pointer to this function should be passed as part of the
+ * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
+ * of this type. It should be followed by a pointer to a value of
+ * type 'char *'.
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (will point to the 'char *',
+ * which will be allocated)
+ * @param option name of the option
+ * @param value actual value of the option (a string)
+ * @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);
+ GNUNET_free_non_null (*val);
+ *val = GNUNET_strdup (value);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Set an option of type 'unsigned long long' from the command line.
+ * A pointer to this function should be passed as part of the
+ * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
+ * of this type. It should be followed by a pointer to a value of
+ * type 'unsigned long long'.
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (will point to the 'unsigned long long')
+ * @param option name of the option
+ * @param value actual value of the option as a string.
+ * @return GNUNET_OK if parsing the value worked
+ */
+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"), option);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
+ * Set an option of type 'unsigned int' from the command line.
+ * A pointer to this function should be passed as part of the
+ * 'struct GNUNET_GETOPT_CommandLineOption' array to initialize options
+ * of this type. It should be followed by a pointer to a value of
+ * type 'unsigned int'.
+ *
+ * @param ctx command line processing context
+ * @param scls additional closure (will point to the 'unsigned int')
+ * @param option name of the option
+ * @param value actual value of the option as a string.
+ * @return GNUNET_OK if parsing the value worked
+ */
+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"), option);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/* end of getopt_helpers.c */
diff --git a/src/util/gnunet-config-diff.c b/src/util/gnunet-config-diff.c
new file mode 100644
index 0000000..207b951
--- /dev/null
+++ b/src/util/gnunet-config-diff.c
@@ -0,0 +1,23 @@
+#include "platform.h"
+#include <gnunet_util_lib.h>
+
+int
+main (int argc, char **argv)
+{
+ struct GNUNET_CONFIGURATION_Handle *i1;
+ struct GNUNET_CONFIGURATION_Handle *i2;
+
+ if (argc != 3)
+ {
+ fprintf (stderr, "Invoke using `%s DEFAULTS-IN DIFFS'\n", argv[0]);
+ return 1;
+ }
+ i1 = GNUNET_CONFIGURATION_create ();
+ i2 = GNUNET_CONFIGURATION_create ();
+ if ((GNUNET_OK != GNUNET_CONFIGURATION_load (i1, argv[1])) ||
+ (GNUNET_OK != GNUNET_CONFIGURATION_load (i2, argv[2])))
+ return 1;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_write_diffs (i1, i2, argv[2]))
+ return 2;
+ return 0;
+}
diff --git a/src/util/gnunet-resolver.c b/src/util/gnunet-resolver.c
new file mode 100644
index 0000000..142dd0d
--- /dev/null
+++ b/src/util/gnunet-resolver.c
@@ -0,0 +1,158 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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/gnunet-resolver.c
+ * @brief tool to test resolver
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_resolver_service.h"
+
+#define GET_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 1)
+
+/**
+ * Flag for reverse lookup.
+ */
+static int reverse;
+
+
+/**
+ * Prints each hostname obtained from DNS.
+ *
+ * @param cls closure (unused)
+ * @param hostname one of the names for the host, NULL
+ * on the last call to the callback
+ */
+static void
+print_hostname (void *cls,
+ const char *hostname)
+{
+ if (NULL == hostname)
+ return;
+ FPRINTF (stdout, "%s\n", hostname);
+}
+
+
+/**
+ * Callback function to display address.
+ *
+ * @param cls closure (unused)
+ * @param addr one of the addresses of the host, NULL for the last address
+ * @param addrlen length of the address
+ */
+static void
+print_sockaddr (void *cls, const struct sockaddr *addr, socklen_t addrlen)
+{
+ if (NULL == addr)
+ return;
+ FPRINTF (stdout, "%s\n", GNUNET_a2s (addr, addrlen));
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure
+ * @param args remaining command-line arguments
+ * @param cfgfile name of the configuration file used (for saving, can be NULL!)
+ * @param cfg configuration
+ */
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ const struct sockaddr *sa;
+ socklen_t salen;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+
+ if (args[0] == NULL)
+ return;
+ if (! reverse)
+ {
+ GNUNET_RESOLVER_ip_get (args[0], AF_UNSPEC, GET_TIMEOUT, &print_sockaddr, NULL);
+ return;
+ }
+
+ sa = NULL;
+ memset (&v4, 0, sizeof (v4));
+ v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ if (1 == inet_pton (AF_INET,
+ args[0],
+ &v4.sin_addr))
+ {
+ sa = (struct sockaddr *) &v4;
+ salen = sizeof (v4);
+ }
+ memset (&v6, 0, sizeof (v6));
+ v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = sizeof (v6);
+#endif
+ if (1 == inet_pton (AF_INET6,
+ args[0],
+ &v6.sin6_addr))
+ {
+ sa = (struct sockaddr *) &v6;
+ salen = sizeof (v6);
+ }
+ if (NULL == sa)
+ {
+ fprintf (stderr,
+ "`%s' is not a valid IP: %s\n",
+ args[0],
+ strerror (errno));
+ return;
+ }
+ GNUNET_RESOLVER_hostname_get (sa, salen,
+ GNUNET_YES,
+ GET_TIMEOUT,
+ &print_hostname,
+ NULL);
+}
+
+/**
+ * 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)
+{
+ static const struct GNUNET_GETOPT_CommandLineOption options[] = {
+ { 'r', "reverse", NULL,
+ gettext_noop ("perform a reverse lookup"),
+ 0, &GNUNET_GETOPT_set_one, &reverse },
+ GNUNET_GETOPT_OPTION_END
+ };
+ return (GNUNET_OK ==
+ GNUNET_PROGRAM_run (argc, argv, "gnunet-resolver [hostname]",
+ gettext_noop ("Use build-in GNUnet stub resolver"),
+ options, &run, NULL)) ? 0 : 1;
+}
+
+/* end of gnunet-resolver.c */
diff --git a/src/util/gnunet-service-resolver.c b/src/util/gnunet-service-resolver.c
new file mode 100644
index 0000000..f20666a
--- /dev/null
+++ b/src/util/gnunet-service-resolver.c
@@ -0,0 +1,584 @@
+/*
+ This file is part of GNUnet.
+ (C) 2007, 2008, 2009, 2012 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/gnunet-service-resolver.c
+ * @brief code to do DNS resolution
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_statistics_service.h"
+#include "resolver.h"
+
+/**
+ * A cached DNS lookup result.
+ */
+struct IPCache
+{
+ /**
+ * This is a doubly linked list.
+ */
+ struct IPCache *next;
+
+ /**
+ * This is a doubly linked list.
+ */
+ struct IPCache *prev;
+
+ /**
+ * Hostname in human-readable form.
+ */
+ char *addr;
+
+ /**
+ * Binary IP address, allocated at the end of this struct.
+ */
+ const void *ip;
+
+ /**
+ * Last time this entry was updated.
+ */
+ struct GNUNET_TIME_Absolute last_refresh;
+
+ /**
+ * Last time this entry was requested.
+ */
+ struct GNUNET_TIME_Absolute last_request;
+
+ /**
+ * Number of bytes in ip.
+ */
+ size_t ip_len;
+
+ /**
+ * Address family of the IP.
+ */
+ int af;
+};
+
+
+/**
+ * Start of the linked list of cached DNS lookup results.
+ */
+static struct IPCache *cache_head;
+
+/**
+ * Tail of the linked list of cached DNS lookup results.
+ */
+static struct IPCache *cache_tail;
+
+
+#if HAVE_GETNAMEINFO
+/**
+ * Resolve the given request using getnameinfo
+ *
+ * @param cache the request to resolve (and where to store the result)
+ */
+static void
+getnameinfo_resolve (struct IPCache *cache)
+{
+ char hostname[256];
+ const struct sockaddr *sa;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ size_t salen;
+
+ switch (cache->af)
+ {
+ case AF_INET:
+ GNUNET_assert (cache->ip_len == sizeof (struct in_addr));
+ sa = (const struct sockaddr*) &v4;
+ memset (&v4, 0, sizeof (v4));
+ v4.sin_addr = * (const struct in_addr*) cache->ip;
+ v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ salen = sizeof (v4);
+ break;
+ case AF_INET6:
+ GNUNET_assert (cache->ip_len == sizeof (struct in6_addr));
+ sa = (const struct sockaddr*) &v6;
+ memset (&v6, 0, sizeof (v6));
+ v6.sin6_addr = * (const struct in6_addr*) cache->ip;
+ v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = sizeof (v6);
+#endif
+ salen = sizeof (v6);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+
+ if (0 ==
+ getnameinfo (sa, salen, hostname, sizeof (hostname), NULL,
+ 0, 0))
+ cache->addr = GNUNET_strdup (hostname);
+}
+#endif
+
+
+#if HAVE_GETHOSTBYADDR
+/**
+ * Resolve the given request using gethostbyaddr
+ *
+ * @param cache the request to resolve (and where to store the result)
+ */
+static void
+gethostbyaddr_resolve (struct IPCache *cache)
+{
+ struct hostent *ent;
+
+ ent = gethostbyaddr (cache->ip,
+ cache->ip_len,
+ cache->af);
+ if (ent != NULL)
+ cache->addr = GNUNET_strdup (ent->h_name);
+}
+#endif
+
+
+/**
+ * Resolve the given request using the available methods.
+ *
+ * @param cache the request to resolve (and where to store the result)
+ */
+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 client handle to the client making the request (for sending the reply)
+ * @param af AF_INET or AF_INET6
+ * @param ip 'struct in_addr' or 'struct in6_addr'
+ */
+static void
+get_ip_as_string (struct GNUNET_SERVER_Client *client,
+ int af,
+ const void *ip)
+{
+ struct IPCache *pos;
+ struct IPCache *next;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_SERVER_TransmitContext *tc;
+ size_t ip_len;
+
+ switch (af)
+ {
+ case AF_INET:
+ ip_len = sizeof (struct in_addr);
+ break;
+ case AF_INET6:
+ ip_len = sizeof (struct in6_addr);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+ now = GNUNET_TIME_absolute_get ();
+ next = cache_head;
+ while ( (NULL != (pos = next)) &&
+ ( (pos->af != af) ||
+ (pos->ip_len != ip_len) ||
+ (0 != memcmp (pos->ip, ip, ip_len))) )
+ {
+ next = pos->next;
+ if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value <
+ 60 * 60 * 1000)
+ {
+ GNUNET_CONTAINER_DLL_remove (cache_head,
+ cache_tail,
+ pos);
+ GNUNET_free_non_null (pos->addr);
+ GNUNET_free (pos);
+ continue;
+ }
+ }
+ if (pos != NULL)
+ {
+ pos->last_request = now;
+ if (GNUNET_TIME_absolute_get_duration (pos->last_request).rel_value <
+ 60 * 60 * 1000)
+ {
+ GNUNET_free_non_null (pos->addr);
+ pos->addr = NULL;
+ cache_resolve (pos);
+ }
+ }
+ else
+ {
+ pos = GNUNET_malloc (sizeof (struct IPCache) + ip_len);
+ pos->ip = &pos[1];
+ memcpy (&pos[1], ip, ip_len);
+ pos->last_request = now;
+ pos->last_refresh = now;
+ pos->ip_len = ip_len;
+ pos->af = af;
+ GNUNET_CONTAINER_DLL_insert (cache_head,
+ cache_tail,
+ pos);
+ cache_resolve (pos);
+ }
+ tc = GNUNET_SERVER_transmit_context_create (client);
+ if (pos->addr != NULL)
+ GNUNET_SERVER_transmit_context_append_data (tc, pos->addr,
+ strlen (pos->addr) + 1,
+ GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
+ GNUNET_SERVER_transmit_context_append_data (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 af)
+{
+ 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 = af;
+#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,
+ (af ==
+ AF_INET) ? "IPv4" : ((af == AF_INET6) ? "IPv6" : "any"),
+ gai_strerror (s));
+ if ((s == EAI_BADFLAGS) || (s == EAI_MEMORY)
+#ifndef MINGW
+ || (s == EAI_SYSTEM)
+#else
+ // FIXME NILS
+ || 1
+#endif
+ )
+ return GNUNET_NO; /* other function may still succeed */
+ return GNUNET_SYSERR;
+ }
+ if (result == NULL)
+ return GNUNET_SYSERR;
+ pos = result;
+ while (pos != NULL)
+ {
+ switch (pos->ai_family)
+ {
+ case AF_INET:
+ GNUNET_SERVER_transmit_context_append_data (tc,
+ &((struct sockaddr_in*) pos->ai_addr)->sin_addr,
+ sizeof (struct in_addr),
+ GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
+ break;
+ case AF_INET6:
+ GNUNET_SERVER_transmit_context_append_data (tc,
+ &((struct sockaddr_in6*) pos->ai_addr)->sin6_addr,
+ sizeof (struct in6_addr),
+ GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
+ break;
+ default:
+ /* unsupported, skip */
+ break;
+ }
+ 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 af)
+{
+ struct hostent *hp;
+ int ret1;
+ int ret2;
+
+ if (af == 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, af);
+ 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 == af);
+ switch (af)
+ {
+ case AF_INET:
+ GNUNET_assert (hp->h_length == sizeof (struct in_addr));
+ GNUNET_SERVER_transmit_context_append_data (tc,
+ hp->h_addr_list[0],
+ hp->h_length,
+ GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
+ break;
+ case AF_INET6:
+ GNUNET_assert (hp->h_length == sizeof (struct in6_addr));
+ GNUNET_SERVER_transmit_context_append_data (tc,
+ hp->h_addr_list[0],
+ hp->h_length,
+ GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE);
+ break;
+ default:
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+#endif
+
+
+#if HAVE_GETHOSTBYNAME
+static int
+gethostbyname_resolve (struct GNUNET_SERVER_TransmitContext *tc,
+ const char *hostname)
+{
+ struct hostent *hp;
+
+ 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));
+ GNUNET_SERVER_transmit_context_append_data (tc,
+ hp->h_addr_list[0],
+ hp->h_length,
+ 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 af 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 af)
+{
+ 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, af);
+#endif
+#if HAVE_GETHOSTBYNAME2
+ if (ret == GNUNET_NO)
+ ret = gethostbyname2_resolve (tc, hostname, af);
+#endif
+#if HAVE_GETHOSTBYNAME
+ if ((ret == GNUNET_NO) && ((af == AF_UNSPEC) || (af == PF_INET)))
+ gethostbyname_resolve (tc, hostname);
+#endif
+ GNUNET_SERVER_transmit_context_append_data (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 client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_get (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ uint16_t msize;
+ const struct GNUNET_RESOLVER_GetMessage *msg;
+ const void *ip;
+ uint16_t size;
+ int direction;
+ int af;
+
+ 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);
+ af = ntohl (msg->af);
+ if (direction == GNUNET_NO)
+ {
+ /* IP from hostname */
+ const char *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, af);
+ return;
+ }
+ ip = &msg[1];
+ switch (af)
+ {
+ case AF_INET:
+ if (size != sizeof (struct in_addr))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ break;
+ case AF_INET6:
+ if (size != sizeof (struct in6_addr))
+ {
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ GNUNET_SERVER_receive_done (client, GNUNET_SYSERR);
+ return;
+ }
+#if DEBUG_RESOLVER
+ {
+ char buf[INET6_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ _("Resolver asked to look up IP address `%s'.\n"),
+ inet_ntop (af, ip, buf, sizeof (buf)));
+ }
+#endif
+ get_ip_as_string (client, af, ip);
+}
+
+
+/**
+ * Process resolver requests.
+ *
+ * @param cls closure
+ * @param server the initialized server
+ * @param cfg configuration to use
+ */
+static void
+run (void *cls, struct GNUNET_SERVER_Handle *server,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ static const struct GNUNET_SERVER_MessageHandler handlers[] = {
+ {&handle_get, NULL, GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST, 0},
+ {NULL, NULL, 0, 0}
+ };
+ 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)
+{
+ struct IPCache *pos;
+ int ret;
+
+ ret =
+ (GNUNET_OK ==
+ GNUNET_SERVICE_run (argc, argv, "resolver", GNUNET_SERVICE_OPTION_NONE,
+ &run, NULL)) ? 0 : 1;
+ while (NULL != (pos = cache_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (cache_head,
+ cache_tail,
+ pos);
+ GNUNET_free_non_null (pos->addr);
+ GNUNET_free (pos);
+ }
+ return ret;
+}
+
+/* end of gnunet-service-resolver.c */
diff --git a/src/util/helper.c b/src/util/helper.c
new file mode 100644
index 0000000..43ec23a
--- /dev/null
+++ b/src/util/helper.c
@@ -0,0 +1,506 @@
+/*
+ This file is part of GNUnet.
+ (C) 2011, 2012 Christian Grothoff
+
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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/helper.c
+ * @brief API for dealing with (SUID) helper processes that communicate via GNUNET_MessageHeaders on stdin/stdout
+ * @author Philipp Toelke
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_util_lib.h"
+
+
+/**
+ * Entry in the queue of messages we need to transmit to the helper.
+ */
+struct HelperMessageQueueEntry
+{
+
+ /**
+ * This is an entry in a DLL.
+ */
+ struct HelperMessageQueueEntry *next;
+
+ /**
+ * This is an entry in a DLL.
+ */
+ struct HelperMessageQueueEntry *prev;
+
+ /**
+ * Message to transmit (allocated at the end of this struct)
+ */
+ const struct GNUNET_MessageHeader *msg;
+
+ /**
+ * Function to call upon completion.
+ */
+ GNUNET_HELPER_Continuation cont;
+
+ /**
+ * Closure to 'cont'.
+ */
+ void *cont_cls;
+
+ /**
+ * Current write position.
+ */
+ unsigned int wpos;
+
+};
+
+
+/**
+ * The handle to a helper process.
+ */
+struct GNUNET_HELPER_Handle
+{
+
+ /**
+ * PipeHandle to receive data from the helper
+ */
+ struct GNUNET_DISK_PipeHandle *helper_in;
+
+ /**
+ * PipeHandle to send data to the helper
+ */
+ struct GNUNET_DISK_PipeHandle *helper_out;
+
+ /**
+ * FileHandle to receive data from the helper
+ */
+ const struct GNUNET_DISK_FileHandle *fh_from_helper;
+
+ /**
+ * FileHandle to send data to the helper
+ */
+ const struct GNUNET_DISK_FileHandle *fh_to_helper;
+
+ /**
+ * The process id of the helper
+ */
+ struct GNUNET_OS_Process *helper_proc;
+
+ /**
+ * The Message-Tokenizer that tokenizes the messages comming from the helper
+ */
+ struct GNUNET_SERVER_MessageStreamTokenizer *mst;
+
+ /**
+ * First message queued for transmission to helper.
+ */
+ struct HelperMessageQueueEntry *mq_head;
+
+ /**
+ * Last message queued for transmission to helper.
+ */
+ struct HelperMessageQueueEntry *mq_tail;
+
+ /**
+ * Binary to run.
+ */
+ const char *binary_name;
+
+ /**
+ * NULL-terminated list of command-line arguments.
+ */
+ char *const *binary_argv;
+
+ /**
+ * Task to read from the helper.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier read_task;
+
+ /**
+ * Task to read from the helper.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier write_task;
+
+ /**
+ * Restart task.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier restart_task;
+};
+
+
+/**
+ * Stop the helper process, we're closing down or had an error.
+ *
+ * @param h handle to the helper process
+ */
+static void
+stop_helper (struct GNUNET_HELPER_Handle *h)
+{
+ struct HelperMessageQueueEntry *qe;
+
+ if (NULL != h->helper_proc)
+ {
+ GNUNET_break (0 == GNUNET_OS_process_kill (h->helper_proc, SIGTERM));
+ GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (h->helper_proc));
+ GNUNET_OS_process_close (h->helper_proc);
+ h->helper_proc = NULL;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != h->restart_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->restart_task);
+ h->restart_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != h->read_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->read_task);
+ h->read_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != h->write_task)
+ {
+ GNUNET_SCHEDULER_cancel (h->write_task);
+ h->write_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (NULL != h->helper_in)
+ {
+ GNUNET_DISK_pipe_close (h->helper_in);
+ h->helper_in = NULL;
+ h->fh_to_helper = NULL;
+ }
+ if (NULL != h->helper_out)
+ {
+ GNUNET_DISK_pipe_close (h->helper_out);
+ h->helper_out = NULL;
+ h->fh_from_helper = NULL;
+ }
+ while (NULL != (qe = h->mq_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (h->mq_head,
+ h->mq_tail,
+ qe);
+ if (NULL != qe->cont)
+ qe->cont (qe->cont_cls, GNUNET_NO);
+ GNUNET_free (qe);
+ }
+ /* purge MST buffer */
+ (void) GNUNET_SERVER_mst_receive (h->mst, NULL, NULL, 0, GNUNET_YES, GNUNET_NO);
+}
+
+
+/**
+ * Restart the helper process.
+ *
+ * @param cls handle to the helper process
+ * @param tc scheduler context
+ */
+static void
+restart_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc);
+
+
+/**
+ * Read from the helper-process
+ *
+ * @param cls handle to the helper process
+ * @param tc scheduler context
+ */
+static void
+helper_read (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_HELPER_Handle *h = cls;
+ char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE];
+ ssize_t t;
+
+ h->read_task = GNUNET_SCHEDULER_NO_TASK;
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+ /* try again */
+ h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ h->fh_from_helper, &helper_read, h);
+ return;
+ }
+ t = GNUNET_DISK_file_read (h->fh_from_helper, &buf, sizeof (buf));
+ if (t < 0)
+ {
+ /* On read-error, restart the helper */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Error reading from `%s': %s\n"),
+ h->binary_name,
+ STRERROR (errno));
+ stop_helper (h);
+ /* Restart the helper */
+ h->restart_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &restart_task, h);
+ return;
+ }
+ if (0 == t)
+ {
+ /* this happens if the helper is shut down via a
+ signal, so it is not a "hard" error */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Got 0 bytes from helper `%s' (EOF)\n"),
+ h->binary_name);
+ stop_helper (h);
+ /* Restart the helper */
+ h->restart_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &restart_task, h);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _("Got %u bytes from helper `%s'\n"),
+ (unsigned int) t,
+ h->binary_name);
+ h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ h->fh_from_helper, &helper_read, h);
+ if (GNUNET_SYSERR ==
+ GNUNET_SERVER_mst_receive (h->mst, NULL, buf, t, GNUNET_NO, GNUNET_NO))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to parse inbound message from helper `%s'\n"),
+ h->binary_name);
+ stop_helper (h);
+ /* Restart the helper */
+ h->restart_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &restart_task, h);
+ return;
+ }
+}
+
+
+/**
+ * Start the helper process.
+ *
+ * @param h handle to the helper process
+ */
+static void
+start_helper (struct GNUNET_HELPER_Handle *h)
+{
+ h->helper_in = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
+ h->helper_out = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
+ if ( (h->helper_in == NULL) || (h->helper_out == NULL))
+ {
+ /* out of file descriptors? try again later... */
+ stop_helper (h);
+ h->restart_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &restart_task, h);
+ return;
+ }
+ h->fh_from_helper =
+ GNUNET_DISK_pipe_handle (h->helper_out, GNUNET_DISK_PIPE_END_READ);
+ h->fh_to_helper =
+ GNUNET_DISK_pipe_handle (h->helper_in, GNUNET_DISK_PIPE_END_WRITE);
+ h->helper_proc =
+ GNUNET_OS_start_process_vap (GNUNET_NO,
+ h->helper_in, h->helper_out,
+ h->binary_name,
+ h->binary_argv);
+ if (NULL == h->helper_proc)
+ {
+ /* failed to start process? try again later... */
+ stop_helper (h);
+ h->restart_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &restart_task, h);
+ return;
+ }
+ GNUNET_DISK_pipe_close_end (h->helper_out, GNUNET_DISK_PIPE_END_WRITE);
+ GNUNET_DISK_pipe_close_end (h->helper_in, GNUNET_DISK_PIPE_END_READ);
+ h->read_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ h->fh_from_helper,
+ &helper_read,
+ h);
+}
+
+
+/**
+ * Restart the helper process.
+ *
+ * @param cls handle to the helper process
+ * @param tc scheduler context
+ */
+static void
+restart_task (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_HELPER_Handle*h = cls;
+
+ h->restart_task = GNUNET_SCHEDULER_NO_TASK;
+ start_helper (h);
+}
+
+
+/**
+ * @brief Starts a helper and begins reading from it
+ *
+ * @param binary_name name of the binary to run
+ * @param binary_argv NULL-terminated list of arguments to give when starting the binary (this
+ * argument must not be modified by the client for
+ * the lifetime of the helper h)
+ * @param cb function to call if we get messages from the helper
+ * @param cb_cls Closure for the callback
+ * @return the new H, NULL on error
+ */
+struct GNUNET_HELPER_Handle*
+GNUNET_HELPER_start (const char *binary_name,
+ char *const binary_argv[],
+ GNUNET_SERVER_MessageTokenizerCallback cb, void *cb_cls)
+{
+ struct GNUNET_HELPER_Handle*h;
+
+ h = GNUNET_malloc (sizeof (struct GNUNET_HELPER_Handle));
+ h->binary_name = binary_name;
+ h->binary_argv = binary_argv;
+ h->mst = GNUNET_SERVER_mst_create (cb, cb_cls);
+ start_helper (h);
+ return h;
+}
+
+
+/**
+ * @brief Kills the helper, closes the pipe and frees the h
+ *
+ * @param h h to helper to stop
+ */
+void
+GNUNET_HELPER_stop (struct GNUNET_HELPER_Handle *h)
+{
+ struct HelperMessageQueueEntry *qe;
+
+ /* signal pending writes that we were stopped */
+ while (NULL != (qe = h->mq_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (h->mq_head,
+ h->mq_tail,
+ qe);
+ if (NULL != qe->cont)
+ qe->cont (qe->cont_cls, GNUNET_SYSERR);
+ GNUNET_free (qe);
+ }
+ stop_helper (h);
+ GNUNET_SERVER_mst_destroy (h->mst);
+ GNUNET_free (h);
+}
+
+
+/**
+ * Write to the helper-process
+ *
+ * @param cls handle to the helper process
+ * @param tc scheduler context
+ */
+static void
+helper_write (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_HELPER_Handle *h = cls;
+ struct HelperMessageQueueEntry *qe;
+ const char *buf;
+ ssize_t t;
+
+ h->write_task = GNUNET_SCHEDULER_NO_TASK;
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+ /* try again */
+ h->write_task = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ h->fh_to_helper, &helper_write, h);
+ return;
+ }
+ if (NULL == (qe = h->mq_head))
+ return; /* how did this happen? */
+ buf = (const char*) qe->msg;
+ t = GNUNET_DISK_file_write (h->fh_to_helper, &buf[qe->wpos], ntohs (qe->msg->size) - qe->wpos);
+ if (t <= 0)
+ {
+ /* On write-error, restart the helper */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ _("Error writing to `%s': %s\n"),
+ h->binary_name,
+ STRERROR (errno));
+ stop_helper (h);
+ /* Restart the helper */
+ h->restart_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
+ &restart_task, h);
+ return;
+ }
+ qe->wpos += t;
+ if (qe->wpos == ntohs (qe->msg->size))
+ {
+ GNUNET_CONTAINER_DLL_remove (h->mq_head,
+ h->mq_tail,
+ qe);
+ if (NULL != qe->cont)
+ qe->cont (qe->cont_cls, GNUNET_YES);
+ GNUNET_free (qe);
+ }
+ if (NULL != h->mq_head)
+ h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ h->fh_to_helper,
+ &helper_write,
+ h);
+}
+
+
+/**
+ * Send an message to the helper.
+ *
+ * @param h helper to send message to
+ * @param msg message to send
+ * @param can_drop can the message be dropped if there is already one in the queue?
+ * @param cont continuation to run once the message is out (PREREQ_DONE on succees, CANCEL
+ * if the helper process died, NULL during GNUNET_HELPER_stop).
+ * @param cont_cls closure for 'cont'
+ * @return GNUNET_YES if the message will be sent
+ * GNUNET_NO if the message was dropped
+ */
+int
+GNUNET_HELPER_send (struct GNUNET_HELPER_Handle *h,
+ const struct GNUNET_MessageHeader *msg,
+ int can_drop,
+ GNUNET_HELPER_Continuation cont,
+ void *cont_cls)
+{
+ struct HelperMessageQueueEntry *qe;
+ uint16_t mlen;
+
+ if (NULL == h->fh_to_helper)
+ return GNUNET_NO;
+ if ( (GNUNET_YES == can_drop) &&
+ (h->mq_head != NULL) )
+ return GNUNET_NO;
+ mlen = ntohs (msg->size);
+ qe = GNUNET_malloc (sizeof (struct HelperMessageQueueEntry) + mlen);
+ qe->msg = (const struct GNUNET_MessageHeader*) &qe[1];
+ memcpy (&qe[1], msg, mlen);
+ qe->cont = cont;
+ qe->cont_cls = cont_cls;
+ GNUNET_CONTAINER_DLL_insert_tail (h->mq_head,
+ h->mq_tail,
+ qe);
+ if (GNUNET_SCHEDULER_NO_TASK == h->write_task)
+ h->write_task = GNUNET_SCHEDULER_add_write_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ h->fh_to_helper,
+ &helper_write,
+ h);
+
+ return GNUNET_YES;
+}
+
+
+/* end of helper.c */
diff --git a/src/util/load.c b/src/util/load.c
new file mode 100644
index 0000000..e978a95
--- /dev/null
+++ b/src/util/load.c
@@ -0,0 +1,260 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010 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/load.c
+ * @brief functions related to load calculations
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_load_lib.h"
+
+#define DEBUG_LOAD GNUNET_EXTRA_LOGGING
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Values we track for load calculations.
+ */
+struct GNUNET_LOAD_Value
+{
+
+ /**
+ * How fast should the load decline if no values are added?
+ */
+ struct GNUNET_TIME_Relative autodecline;
+
+ /**
+ * Last time this load value was updated by an event.
+ */
+ struct GNUNET_TIME_Absolute last_update;
+
+ /**
+ * Sum of all datastore delays ever observed (in ms). Note that
+ * delays above 64k ms are excluded (to avoid overflow within
+ * first 4 billion requests).
+ */
+ uint64_t cummulative_delay;
+
+ /**
+ * Sum of squares of all datastore delays ever observed (in ms). Note that
+ * delays above 64k ms are excluded (to avoid overflow within
+ * first 4 billion requests).
+ */
+ uint64_t cummulative_squared_delay;
+
+ /**
+ * Total number of requests included in the cummulative datastore delay values.
+ */
+ uint64_t cummulative_request_count;
+
+ /**
+ * Current running average datastore delay. Its relation to the
+ * average datastore delay and it std. dev. (as calcualted from the
+ * cummulative values) tells us our current load.
+ */
+ double runavg_delay;
+
+ /**
+ * How high is the load? 0 for below average, otherwise
+ * the number of std. devs we are above average, or 100 if the
+ * load is so high that we currently cannot calculate it.
+ */
+ double load;
+
+};
+
+
+static void
+internal_update (struct GNUNET_LOAD_Value *load)
+{
+ struct GNUNET_TIME_Relative delta;
+ unsigned int n;
+
+ if (load->autodecline.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+ return;
+ delta = GNUNET_TIME_absolute_get_duration (load->last_update);
+ if (delta.rel_value < load->autodecline.rel_value)
+ return;
+ if (load->autodecline.rel_value == 0)
+ {
+ load->runavg_delay = 0.0;
+ load->load = 0;
+ return;
+ }
+ n = delta.rel_value / load->autodecline.rel_value;
+ if (n > 16)
+ {
+ load->runavg_delay = 0.0;
+ load->load = 0;
+ return;
+ }
+ while (n > 0)
+ {
+ n--;
+ load->runavg_delay = (load->runavg_delay * 7.0) / 8.0;
+ }
+}
+
+
+/**
+ * Create a new load value.
+ *
+ * @param autodecline speed at which this value should automatically
+ * decline in the absence of external events; at the given
+ * frequency, 0-load values will be added to the load
+ * @return the new load value
+ */
+struct GNUNET_LOAD_Value *
+GNUNET_LOAD_value_init (struct GNUNET_TIME_Relative autodecline)
+{
+ struct GNUNET_LOAD_Value *ret;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_LOAD_Value));
+ ret->autodecline = autodecline;
+ ret->last_update = GNUNET_TIME_absolute_get ();
+ return ret;
+}
+
+
+/**
+ * Change the value by which the load automatically declines.
+ *
+ * @param load load to update
+ * @param autodecline frequency of load decline
+ */
+void
+GNUNET_LOAD_value_set_decline (struct GNUNET_LOAD_Value *load,
+ struct GNUNET_TIME_Relative autodecline)
+{
+ internal_update (load);
+ load->autodecline = autodecline;
+}
+
+
+/**
+ * Recalculate our load value.
+ *
+ * @param load load to update
+ */
+static void
+calculate_load (struct GNUNET_LOAD_Value *load)
+{
+ double stddev;
+ double avgdel;
+ double sum_val_i;
+ double n;
+ double nm1;
+
+ if (load->cummulative_request_count <= 1)
+ return;
+ /* calcuate std dev of latency; we have for n values of "i" that:
+ *
+ * avg = (sum val_i) / n
+ * stddev = (sum (val_i - avg)^2) / (n-1)
+ * = (sum (val_i^2 - 2 avg val_i + avg^2) / (n-1)
+ * = (sum (val_i^2) - 2 avg sum (val_i) + n * avg^2) / (n-1)
+ */
+ sum_val_i = (double) load->cummulative_delay;
+ n = ((double) load->cummulative_request_count);
+ nm1 = n - 1.0;
+ avgdel = sum_val_i / n;
+ stddev =
+ (((double) load->cummulative_squared_delay) - 2.0 * avgdel * sum_val_i +
+ n * avgdel * avgdel) / nm1;
+ if (stddev <= 0)
+ stddev = 0.01; /* must have been rounding error or zero; prevent division by zero */
+ /* now calculate load based on how far out we are from
+ * std dev; or if we are below average, simply assume load zero */
+ if (load->runavg_delay < avgdel)
+ load->load = 0.0;
+ else
+ load->load = (load->runavg_delay - avgdel) / stddev;
+}
+
+
+/**
+ * Get the current load.
+ *
+ * @param load load handle
+ * @return zero for below-average load, otherwise
+ * number of std. devs we are above average;
+ * 100 if the latest updates were so large
+ * that we could not do proper calculations
+ */
+double
+GNUNET_LOAD_get_load (struct GNUNET_LOAD_Value *load)
+{
+ internal_update (load);
+ calculate_load (load);
+ return load->load;
+}
+
+
+/**
+ * Get the average value given to update so far.
+ *
+ * @param load load handle
+ * @return zero if update was never called
+ */
+double
+GNUNET_LOAD_get_average (struct GNUNET_LOAD_Value *load)
+{
+ double n;
+ double sum_val_i;
+
+ internal_update (load);
+ if (load->cummulative_request_count == 0)
+ return 0.0;
+ n = ((double) load->cummulative_request_count);
+ sum_val_i = (double) load->cummulative_delay;
+ return sum_val_i / n;
+}
+
+
+/**
+ * Update the current load.
+ *
+ * @param load to update
+ * @param data latest measurement value (for example, delay)
+ */
+void
+GNUNET_LOAD_update (struct GNUNET_LOAD_Value *load, uint64_t data)
+{
+ uint32_t dv;
+
+ internal_update (load);
+ load->last_update = GNUNET_TIME_absolute_get ();
+ if (data > 64 * 1024)
+ {
+ /* very large */
+ load->load = 100.0;
+ return;
+ }
+ dv = (uint32_t) data;
+ load->cummulative_delay += dv;
+ load->cummulative_squared_delay += dv * dv;
+ load->cummulative_request_count++;
+ load->runavg_delay = ((load->runavg_delay * 7.0) + dv) / 8.0;
+}
+
+
+
+/* end of load.c */
diff --git a/src/util/network.c b/src/util/network.c
new file mode 100644
index 0000000..e530ab7
--- /dev/null
+++ b/src/util/network.c
@@ -0,0 +1,1662 @@
+/*
+ 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.c
+ * @brief basic, low-level networking interface
+ * @author Nils Durner
+ */
+
+#include "platform.h"
+#include "gnunet_disk_lib.h"
+#include "disk.h"
+#include "gnunet_container_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define DEBUG_NETWORK GNUNET_EXTRA_LOGGING
+
+
+#ifndef INVALID_SOCKET
+#define INVALID_SOCKET -1
+#endif
+
+
+struct GNUNET_NETWORK_Handle
+{
+#ifndef MINGW
+ int fd;
+#else
+ SOCKET fd;
+#endif
+
+ /**
+ * Address family / domain.
+ */
+ int af;
+
+ /**
+ * Number of bytes in addr.
+ */
+ socklen_t addrlen;
+
+ /**
+ * Address we were bound to, or NULL.
+ */
+ struct sockaddr *addr;
+
+};
+
+
+#ifndef FD_COPY
+#define FD_COPY(s, d) (memcpy ((d), (s), sizeof (fd_set)))
+#endif
+
+
+/**
+ * Set if a socket should use blocking or non-blocking IO.
+ * @param fd socket
+ * @param doBlock blocking mode
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+static int
+socket_set_blocking (struct GNUNET_NETWORK_Handle *fd, int doBlock)
+{
+
+#if MINGW
+ u_long mode;
+
+ mode = !doBlock;
+ if (ioctlsocket (fd->fd, FIONBIO, &mode) == SOCKET_ERROR)
+
+ {
+ SetErrnoFromWinsockError (WSAGetLastError ());
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "ioctlsocket");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+
+#else
+ /* not MINGW */
+ int flags = fcntl (fd->fd, F_GETFL);
+
+ if (flags == -1)
+
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "fcntl");
+ return GNUNET_SYSERR;
+ }
+ if (doBlock)
+ flags &= ~O_NONBLOCK;
+
+ else
+ flags |= O_NONBLOCK;
+ if (0 != fcntl (fd->fd, F_SETFL, flags))
+
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "fcntl");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+#endif
+}
+
+
+#ifndef MINGW
+/**
+ * Make a socket non-inheritable to child processes
+ *
+ * @param h the socket to make non-inheritable
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ * @warning Not implemented on Windows
+ */
+static int
+socket_set_inheritable (const struct GNUNET_NETWORK_Handle *h)
+{
+ int i;
+
+ i = fcntl (h->fd, F_GETFD);
+ if (i < 0)
+ return GNUNET_SYSERR;
+ if (i == (i | FD_CLOEXEC))
+ return GNUNET_OK;
+ i |= FD_CLOEXEC;
+ if (fcntl (h->fd, F_SETFD, i) < 0)
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+#endif
+
+
+#ifdef DARWIN
+/**
+ * The MSG_NOSIGNAL equivalent on Mac OS X
+ *
+ * @param h the socket to make non-delaying
+ */
+static void
+socket_set_nosigpipe (const struct GNUNET_NETWORK_Handle *h)
+{
+ int abs_value = 1;
+
+ if (0 !=
+ setsockopt (h->fd, SOL_SOCKET, SO_NOSIGPIPE, &abs_value,
+ sizeof (abs_value)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+}
+#endif
+
+
+/**
+ * Disable delays when sending data via the socket.
+ * (GNUnet makes sure that messages are as big as
+ * possible already).
+ *
+ * @param h the socket to make non-delaying
+ */
+static void
+socket_set_nodelay (const struct GNUNET_NETWORK_Handle *h)
+{
+#ifndef WINDOWS
+ int value = 1;
+
+ if (0 != setsockopt (h->fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof (value)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+#else
+ const char *abs_value = "1";
+
+ if (0 !=
+ setsockopt (h->fd, IPPROTO_TCP, TCP_NODELAY, abs_value,
+ sizeof (abs_value)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+#endif
+}
+
+
+/**
+ * accept a new connection on a socket
+ *
+ * @param desc bound socket
+ * @param address address of the connecting peer, may be NULL
+ * @param address_len length of address
+ * @return client socket
+ */
+struct GNUNET_NETWORK_Handle *
+GNUNET_NETWORK_socket_accept (const struct GNUNET_NETWORK_Handle *desc,
+ struct sockaddr *address, socklen_t * address_len)
+{
+ struct GNUNET_NETWORK_Handle *ret;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));
+#if DEBUG_NETWORK
+ {
+ struct sockaddr name;
+ socklen_t namelen = sizeof (name);
+ int gsn = getsockname (desc->fd, &name, &namelen);
+
+ if (gsn == 0)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Accepting connection on `%s'\n",
+ GNUNET_a2s (&name, namelen));
+ }
+#endif
+ ret->fd = accept (desc->fd, address, address_len);
+ if (address != NULL)
+ ret->af = address->sa_family;
+ else
+ ret->af = desc->af;
+ if (ret->fd == INVALID_SOCKET)
+ {
+#ifdef MINGW
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ GNUNET_free (ret);
+ return NULL;
+ }
+#ifndef MINGW
+ if (ret->fd >= FD_SETSIZE)
+ {
+ GNUNET_break (0 == close (ret->fd));
+ GNUNET_free (ret);
+ errno = EMFILE;
+ return NULL;
+ }
+#endif
+ if (GNUNET_SYSERR == socket_set_blocking (ret, GNUNET_NO))
+
+ {
+
+ /* we might want to treat this one as fatal... */
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret));
+ return NULL;
+ }
+
+#ifndef MINGW
+ if (GNUNET_OK != socket_set_inheritable (ret))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "socket_set_inheritable");
+#endif
+#ifdef DARWIN
+ socket_set_nosigpipe (ret);
+#endif
+#ifdef AF_UNIX
+ if (ret->af != AF_UNIX)
+#endif
+ socket_set_nodelay (ret);
+ return ret;
+}
+
+
+/**
+ * Bind to a connected socket
+ * @param desc socket
+ * @param address address to be bound
+ * @param address_len length of address
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_bind (struct GNUNET_NETWORK_Handle *desc,
+ const struct sockaddr *address,
+ socklen_t address_len)
+{
+ int ret;
+
+#ifdef IPV6_V6ONLY
+#ifdef IPPROTO_IPV6
+ const int on = 1;
+
+ if (desc->af == AF_INET6)
+ if (0 != setsockopt (desc->fd, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, "setsockopt");
+#endif
+#endif
+#ifndef WINDOWS
+ /* This is required, and required here, but only on UNIX */
+ if (0 != setsockopt (desc->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_DEBUG, "setsockopt");
+#endif
+#ifndef LINUX
+#ifndef MINGW
+ if (address->sa_family == AF_UNIX)
+ {
+ const struct sockaddr_un *un = (const struct sockaddr_un *) address;
+
+ (void) unlink (un->sun_path);
+ }
+#endif
+#endif
+ ret = bind (desc->fd, address, address_len);
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ if (ret != 0)
+ return GNUNET_SYSERR;
+#ifndef MINGW
+#ifndef LINUX
+ desc->addr = GNUNET_malloc (address_len);
+ memcpy (desc->addr, address, address_len);
+ desc->addrlen = address_len;
+#endif
+#endif
+ return GNUNET_OK;
+}
+
+
+/**
+ * Close a socket
+ * @param desc socket
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_close (struct GNUNET_NETWORK_Handle *desc)
+{
+ int ret;
+
+#ifdef MINGW
+ DWORD error = 0;
+
+ SetLastError (0);
+ ret = closesocket (desc->fd);
+ error = WSAGetLastError ();
+ SetErrnoFromWinsockError (error);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Closed 0x%x, closesocket() returned %d, GLE is %u\n", desc->fd, ret,
+ error);
+#else
+ ret = close (desc->fd);
+#endif
+#ifndef LINUX
+#ifndef MINGW
+ if ((desc->af == AF_UNIX) && (NULL != desc->addr))
+ {
+ const struct sockaddr_un *un = (const struct sockaddr_un *) desc->addr;
+
+ if (0 != unlink (un->sun_path))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", un->sun_path);
+ }
+#endif
+#endif
+ GNUNET_free_non_null (desc->addr);
+ GNUNET_free (desc);
+ return (ret == 0) ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Box a native socket (and check that it is a socket).
+ *
+ * @param fd socket to box
+ * @return NULL on error (including not supported on target platform)
+ */
+struct GNUNET_NETWORK_Handle *
+GNUNET_NETWORK_socket_box_native (SOCKTYPE fd)
+{
+ struct GNUNET_NETWORK_Handle *ret;
+#if MINGW
+ unsigned long i;
+ DWORD d;
+ /* FIXME: Find a better call to check that FD is valid */
+ if (WSAIoctl (fd, FIONBIO, (void *) &i, sizeof (i), NULL, 0, &d, NULL, NULL) != 0)
+ return NULL; /* invalid FD */
+ ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));
+ ret->fd = fd;
+ ret->af = AF_UNSPEC;
+ return ret;
+#else
+ if (fcntl (fd, F_GETFD) < 0)
+ return NULL; /* invalid FD */
+ ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));
+ ret->fd = fd;
+ ret->af = AF_UNSPEC;
+ return ret;
+#endif
+}
+
+
+/**
+ * Connect a socket
+ * @param desc socket
+ * @param address peer address
+ * @param address_len length of address
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_connect (const struct GNUNET_NETWORK_Handle *desc,
+ const struct sockaddr *address,
+ socklen_t address_len)
+{
+ int ret;
+
+ ret = connect (desc->fd, address, address_len);
+
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ {
+ SetErrnoFromWinsockError (WSAGetLastError ());
+ if (errno == EWOULDBLOCK)
+ errno = EINPROGRESS;
+ }
+#endif
+ return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Get socket options
+ *
+ * @param desc socket
+ * @param level protocol level of the option
+ * @param optname identifier of the option
+ * @param optval options
+ * @param optlen length of optval
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_getsockopt (const struct GNUNET_NETWORK_Handle *desc,
+ int level, int optname, void *optval,
+ socklen_t * optlen)
+{
+ int ret;
+
+ ret = getsockopt (desc->fd, level, optname, optval, optlen);
+
+#ifdef MINGW
+ if (ret == 0 && level == SOL_SOCKET && optname == SO_ERROR)
+ *((int *) optval) = GetErrnoFromWinsockError (*((int *) optval));
+
+ else if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Listen on a socket
+ * @param desc socket
+ * @param backlog length of the listen queue
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_listen (const struct GNUNET_NETWORK_Handle *desc,
+ int backlog)
+{
+ int ret;
+
+ ret = listen (desc->fd, backlog);
+
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+
+#endif
+ return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * How much data is available to be read on this descriptor?
+ *
+ * Returns GNUNET_NO if no data is available, or on error!
+ * @param desc socket
+ */
+ssize_t
+GNUNET_NETWORK_socket_recvfrom_amount (const struct GNUNET_NETWORK_Handle *
+ desc)
+{
+ int error;
+
+ /* How much is there to be read? */
+#ifndef WINDOWS
+ int pending;
+
+ error = ioctl (desc->fd, FIONREAD, &pending);
+ if (error == 0)
+#else
+ u_long pending;
+
+ error = ioctlsocket (desc->fd, FIONREAD, &pending);
+ if (error != SOCKET_ERROR)
+#endif
+ return pending;
+ else
+ return GNUNET_NO;
+}
+
+
+/**
+ * Read data from a connected socket (always non-blocking).
+ * @param desc socket
+ * @param buffer buffer
+ * @param length length of buffer
+ * @param src_addr either the source to recv from, or all zeroes
+ * to be filled in by recvfrom
+ * @param addrlen length of the addr
+ */
+ssize_t
+GNUNET_NETWORK_socket_recvfrom (const struct GNUNET_NETWORK_Handle * desc,
+ void *buffer, size_t length,
+ struct sockaddr * src_addr, socklen_t * addrlen)
+{
+ int ret;
+ int flags;
+
+ flags = 0;
+
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+
+#endif
+ ret = recvfrom (desc->fd, buffer, length, flags, src_addr, addrlen);
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ return ret;
+}
+
+
+/**
+ * Read data from a connected socket (always non-blocking).
+ * @param desc socket
+ * @param buffer buffer
+ * @param length length of buffer
+ */
+ssize_t
+GNUNET_NETWORK_socket_recv (const struct GNUNET_NETWORK_Handle * desc,
+ void *buffer, size_t length)
+{
+ int ret;
+ int flags;
+
+ flags = 0;
+
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+ ret = recv (desc->fd, buffer, length, flags);
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ return ret;
+}
+
+
+/**
+ * Send data (always non-blocking).
+ *
+ * @param desc socket
+ * @param buffer data to send
+ * @param length size of the buffer
+ * @return number of bytes sent, GNUNET_SYSERR on error
+ */
+ssize_t
+GNUNET_NETWORK_socket_send (const struct GNUNET_NETWORK_Handle * desc,
+ const void *buffer, size_t length)
+{
+ int ret;
+ int flags;
+
+ flags = 0;
+
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+
+#endif
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+
+#endif
+ ret = send (desc->fd, buffer, length, flags);
+
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+
+#endif
+ return ret;
+}
+
+
+/**
+ * Send data to a particular destination (always non-blocking).
+ * This function only works for UDP sockets.
+ *
+ * @param desc socket
+ * @param message data to send
+ * @param length size of the data
+ * @param dest_addr destination address
+ * @param dest_len length of address
+ * @return number of bytes sent, GNUNET_SYSERR on error
+ */
+ssize_t
+GNUNET_NETWORK_socket_sendto (const struct GNUNET_NETWORK_Handle * desc,
+ const void *message, size_t length,
+ const struct sockaddr * dest_addr,
+ socklen_t dest_len)
+{
+ int ret;
+ int flags;
+
+ flags = 0;
+
+#ifdef MSG_DONTWAIT
+ flags |= MSG_DONTWAIT;
+#endif
+#ifdef MSG_NOSIGNAL
+ flags |= MSG_NOSIGNAL;
+#endif
+ ret = sendto (desc->fd, message, length, flags, dest_addr, dest_len);
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ return ret;
+}
+
+
+/**
+ * Set socket option
+ * @param fd socket
+ * @param level protocol level of the option
+ * @param option_name option identifier
+ * @param option_value value to set
+ * @param option_len size of option_value
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_setsockopt (struct GNUNET_NETWORK_Handle *fd, int level,
+ int option_name, const void *option_value,
+ socklen_t option_len)
+{
+ int ret;
+
+ ret = setsockopt (fd->fd, level, option_name, option_value, option_len);
+#ifdef MINGW
+ if (SOCKET_ERROR == ret)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Create a new socket. Configure it for non-blocking IO and
+ * mark it as non-inheritable to child processes (set the
+ * close-on-exec flag).
+ *
+ * @param domain domain of the socket
+ * @param type socket type
+ * @param protocol network protocol
+ * @return new socket, NULL on error
+ */
+struct GNUNET_NETWORK_Handle *
+GNUNET_NETWORK_socket_create (int domain, int type, int protocol)
+{
+ struct GNUNET_NETWORK_Handle *ret;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle));
+ ret->af = domain;
+ ret->fd = socket (domain, type, protocol);
+ if (INVALID_SOCKET == ret->fd)
+ {
+#ifdef MINGW
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ GNUNET_free (ret);
+ return NULL;
+ }
+
+#ifndef MINGW
+ if (ret->fd >= FD_SETSIZE)
+ {
+ GNUNET_break (0 == close (ret->fd));
+ GNUNET_free (ret);
+ errno = EMFILE;
+ return NULL;
+ }
+
+#endif
+ if (GNUNET_SYSERR == socket_set_blocking (ret, GNUNET_NO))
+ {
+ /* we might want to treat this one as fatal... */
+ GNUNET_break (0);
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (ret));
+ return NULL;
+ }
+
+#ifndef MINGW
+ if (GNUNET_OK != socket_set_inheritable (ret))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "socket_set_inheritable");
+#endif
+#ifdef DARWIN
+ socket_set_nosigpipe (ret);
+#endif
+ if ((type == SOCK_STREAM)
+#ifdef AF_UNIX
+ && (domain != AF_UNIX)
+#endif
+ )
+ socket_set_nodelay (ret);
+ return ret;
+}
+
+
+/**
+ * Shut down socket operations
+ * @param desc socket
+ * @param how type of shutdown
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_shutdown (struct GNUNET_NETWORK_Handle *desc, int how)
+{
+ int ret;
+
+ ret = shutdown (desc->fd, how);
+#ifdef MINGW
+ if (ret != 0)
+ SetErrnoFromWinsockError (WSAGetLastError ());
+#endif
+ return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Disable the "CORK" feature for communication with the given socket,
+ * forcing the OS to immediately flush the buffer on transmission
+ * instead of potentially buffering multiple messages. Essentially
+ * reduces the OS send buffers to zero.
+ *
+ * @param desc socket
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_NETWORK_socket_disable_corking (struct GNUNET_NETWORK_Handle *desc)
+{
+ int ret = 0;
+
+#if WINDOWS
+ int value = 0;
+
+ if (0 !=
+ (ret =
+ setsockopt (desc->fd, SOL_SOCKET, SO_SNDBUF, (char *) &value,
+ sizeof (value))))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+ if (0 !=
+ (ret =
+ setsockopt (desc->fd, SOL_SOCKET, SO_RCVBUF, (char *) &value,
+ sizeof (value))))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+#elif LINUX
+ int value = 0;
+
+ if (0 !=
+ (ret =
+ setsockopt (desc->fd, SOL_SOCKET, SO_SNDBUF, &value, sizeof (value))))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+ if (0 !=
+ (ret =
+ setsockopt (desc->fd, SOL_SOCKET, SO_RCVBUF, &value, sizeof (value))))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "setsockopt");
+#endif
+ return ret == 0 ? GNUNET_OK : GNUNET_SYSERR;
+}
+
+
+/**
+ * Reset FD set
+ * @param fds fd set
+ */
+void
+GNUNET_NETWORK_fdset_zero (struct GNUNET_NETWORK_FDSet *fds)
+{
+ FD_ZERO (&fds->sds);
+ fds->nsds = 0;
+#ifdef MINGW
+ GNUNET_CONTAINER_slist_clear (fds->handles);
+#endif
+}
+
+/**
+ * Add a socket to the FD set
+ * @param fds fd set
+ * @param desc socket to add
+ */
+void
+GNUNET_NETWORK_fdset_set (struct GNUNET_NETWORK_FDSet *fds,
+ const struct GNUNET_NETWORK_Handle *desc)
+{
+ FD_SET (desc->fd, &fds->sds);
+ if (desc->fd + 1 > fds->nsds)
+ fds->nsds = desc->fd + 1;
+}
+
+
+/**
+ * Check whether a socket is part of the fd set
+ * @param fds fd set
+ * @param desc socket
+ * @return 0 if the FD is not set
+ */
+int
+GNUNET_NETWORK_fdset_isset (const struct GNUNET_NETWORK_FDSet *fds,
+ const struct GNUNET_NETWORK_Handle *desc)
+{
+ return FD_ISSET (desc->fd, &fds->sds);
+}
+
+
+/**
+ * Add one fd set to another
+ * @param dst the fd set to add to
+ * @param src the fd set to add from
+ */
+void
+GNUNET_NETWORK_fdset_add (struct GNUNET_NETWORK_FDSet *dst,
+ const struct GNUNET_NETWORK_FDSet *src)
+{
+ int nfds;
+
+ for (nfds = src->nsds; nfds > 0; nfds--)
+ if (FD_ISSET (nfds, &src->sds))
+
+ {
+ FD_SET (nfds, &dst->sds);
+ if (nfds + 1 > dst->nsds)
+ dst->nsds = nfds + 1;
+ }
+#ifdef MINGW
+ GNUNET_CONTAINER_slist_append (dst->handles, src->handles);
+#endif
+}
+
+
+/**
+ * Copy one fd set to another
+ *
+ * @param to destination
+ * @param from source
+ */
+void
+GNUNET_NETWORK_fdset_copy (struct GNUNET_NETWORK_FDSet *to,
+ const struct GNUNET_NETWORK_FDSet *from)
+{
+ FD_COPY (&from->sds, &to->sds);
+ to->nsds = from->nsds;
+
+#ifdef MINGW
+ GNUNET_CONTAINER_slist_clear (to->handles);
+ GNUNET_CONTAINER_slist_append (to->handles, from->handles);
+#endif
+}
+
+
+/**
+ * Return file descriptor for this network handle
+ *
+ * @param desc wrapper to process
+ * @return POSIX file descriptor
+ */
+int
+GNUNET_NETWORK_get_fd (struct GNUNET_NETWORK_Handle *desc)
+{
+ return desc->fd;
+}
+
+
+/**
+ * Copy a native fd set
+ *
+ * @param to destination
+ * @param from native source set
+ * @param nfds the biggest socket number in from + 1
+ */
+void
+GNUNET_NETWORK_fdset_copy_native (struct GNUNET_NETWORK_FDSet *to,
+ const fd_set * from, int nfds)
+{
+ FD_COPY (from, &to->sds);
+ to->nsds = nfds;
+}
+
+
+/**
+ * Set a native fd in a set
+ *
+ * @param to destination
+ * @param nfd native FD to set
+ */
+void
+GNUNET_NETWORK_fdset_set_native (struct GNUNET_NETWORK_FDSet *to, int nfd)
+{
+ GNUNET_assert ((nfd >= 0) && (nfd < FD_SETSIZE));
+ FD_SET (nfd, &to->sds);
+ to->nsds = GNUNET_MAX (nfd + 1, to->nsds);
+}
+
+
+/**
+ * Test native fd in a set
+ *
+ * @param to set to test, NULL for empty set
+ * @param nfd native FD to test, or -1 for none
+ * @return GNUNET_YES if FD is set in the set
+ */
+int
+GNUNET_NETWORK_fdset_test_native (const struct GNUNET_NETWORK_FDSet *to,
+ int nfd)
+{
+ if ((nfd == -1) || (to == NULL))
+ return GNUNET_NO;
+ return FD_ISSET (nfd, &to->sds) ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
+ * Add a file handle to the fd set
+ * @param fds fd set
+ * @param h the file handle to add
+ */
+void
+GNUNET_NETWORK_fdset_handle_set (struct GNUNET_NETWORK_FDSet *fds,
+ const struct GNUNET_DISK_FileHandle *h)
+{
+#ifdef MINGW
+ GNUNET_CONTAINER_slist_add (fds->handles,
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT, h,
+ sizeof (struct GNUNET_DISK_FileHandle));
+
+#else
+ int fd;
+
+ GNUNET_DISK_internal_file_handle_ (h, &fd, sizeof (int));
+ FD_SET (fd, &fds->sds);
+ if (fd + 1 > fds->nsds)
+ fds->nsds = fd + 1;
+
+#endif
+}
+
+
+/**
+ * Check if a file handle is part of an fd set
+ * @param fds fd set
+ * @param h file handle
+ * @return GNUNET_YES if the file handle is part of the set
+ */
+int
+GNUNET_NETWORK_fdset_handle_isset (const struct GNUNET_NETWORK_FDSet *fds,
+ const struct GNUNET_DISK_FileHandle *h)
+{
+
+#ifdef MINGW
+ return GNUNET_CONTAINER_slist_contains (fds->handles, h,
+ sizeof (struct
+ GNUNET_DISK_FileHandle));
+#else
+ return FD_ISSET (h->fd, &fds->sds);
+#endif
+}
+
+
+/**
+ * Checks if two fd sets overlap
+ * @param fds1 first fd set
+ * @param fds2 second fd set
+ * @return GNUNET_YES if they do overlap, GNUNET_NO otherwise
+ */
+int
+GNUNET_NETWORK_fdset_overlap (const struct GNUNET_NETWORK_FDSet *fds1,
+ const struct GNUNET_NETWORK_FDSet *fds2)
+{
+#ifndef MINGW
+ int nfds;
+
+ nfds = fds1->nsds;
+ if (nfds > fds2->nsds)
+ nfds = fds2->nsds;
+ while (nfds > 0)
+ {
+ nfds--;
+ if (FD_ISSET (nfds, &fds1->sds) && FD_ISSET (nfds, &fds2->sds))
+ return GNUNET_YES;
+ }
+#else
+ struct GNUNET_CONTAINER_SList_Iterator it;
+ struct GNUNET_DISK_FileHandle *h;
+ int i;
+ int j;
+
+ /*This code is somewhat hacky, we are not supposed to know what's
+ * inside of fd_set; also the O(n^2) is really bad... */
+
+ for (i = 0; i < fds1->sds.fd_count; i++)
+ {
+ for (j = 0; j < fds2->sds.fd_count; j++)
+ {
+ if (fds1->sds.fd_array[i] == fds2->sds.fd_array[j])
+ return GNUNET_YES;
+ }
+ }
+ it = GNUNET_CONTAINER_slist_begin (fds1->handles);
+ while (GNUNET_CONTAINER_slist_end (&it) != GNUNET_YES)
+ {
+#if DEBUG_NETWORK
+ struct GNUNET_CONTAINER_SList_Iterator t;
+#endif
+ h = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&it,
+ NULL);
+#if DEBUG_NETWORK
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Checking that FD 0x%x is in another set:\n",
+ h->h);
+ for (t = GNUNET_CONTAINER_slist_begin (fds2->handles);
+ GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&t))
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+
+ fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&t,
+ NULL);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "0x%x\n", fh->h);
+ }
+#endif
+ if (GNUNET_CONTAINER_slist_contains
+ (fds2->handles, h, sizeof (struct GNUNET_DISK_FileHandle)))
+ {
+#if DEBUG_NETWORK
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Match!\n");
+#endif
+ return GNUNET_YES;
+ }
+ GNUNET_CONTAINER_slist_next (&it);
+ }
+#endif
+ return GNUNET_NO;
+}
+
+
+/**
+ * Creates an fd set
+ * @return a new fd set
+ */
+struct GNUNET_NETWORK_FDSet *
+GNUNET_NETWORK_fdset_create ()
+{
+ struct GNUNET_NETWORK_FDSet *fds;
+
+ fds = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_FDSet));
+#ifdef MINGW
+ fds->handles = GNUNET_CONTAINER_slist_create ();
+#endif
+ GNUNET_NETWORK_fdset_zero (fds);
+ return fds;
+}
+
+
+/**
+ * Releases the associated memory of an fd set
+ * @param fds fd set
+ */
+void
+GNUNET_NETWORK_fdset_destroy (struct GNUNET_NETWORK_FDSet *fds)
+{
+#ifdef MINGW
+ GNUNET_CONTAINER_slist_destroy (fds->handles);
+#endif
+ GNUNET_free (fds);
+}
+
+/**
+ * Check if sockets meet certain conditions
+ * @param rfds set of sockets to be checked for readability
+ * @param wfds set of sockets to be checked for writability
+ * @param efds set of sockets to be checked for exceptions
+ * @param timeout relative value when to return
+ * @return number of selected sockets, GNUNET_SYSERR on error
+ */
+int
+GNUNET_NETWORK_socket_select (struct GNUNET_NETWORK_FDSet *rfds,
+ struct GNUNET_NETWORK_FDSet *wfds,
+ struct GNUNET_NETWORK_FDSet *efds,
+ const struct GNUNET_TIME_Relative timeout)
+{
+ int nfds = 0;
+
+#ifdef MINGW
+ int handles = 0;
+ int ex_handles = 0;
+ int read_handles = 0;
+ int write_handles = 0;
+
+ int i = 0;
+ int retcode = 0;
+ DWORD ms_total = 0;
+
+ int nsock = 0, nhandles = 0, nSockEvents = 0;
+
+ static HANDLE hEventRead = 0;
+ static HANDLE hEventWrite = 0;
+ static HANDLE hEventException = 0;
+ static HANDLE hEventPipeWrite = 0;
+ static HANDLE hEventReadReady = 0;
+
+ int readPipes = 0;
+ int writePipePos = 0;
+
+ HANDLE handle_array[FD_SETSIZE + 2];
+ int returncode = -1;
+ DWORD newretcode = 0;
+ int returnedpos = 0;
+
+ struct GNUNET_CONTAINER_SList *handles_read, *handles_write, *handles_except;
+
+ fd_set aread, awrite, aexcept;
+
+#if DEBUG_NETWORK
+ fd_set bread, bwrite, bexcept;
+#endif
+
+ /* TODO: Make this growable */
+ struct GNUNET_DISK_FileHandle *readArray[50];
+#else
+ struct timeval tv;
+#endif
+ if (NULL != rfds)
+ {
+ nfds = rfds->nsds;
+#ifdef MINGW
+ handles += read_handles = GNUNET_CONTAINER_slist_count (rfds->handles);
+#if DEBUG_NETWORK
+ {
+ struct GNUNET_CONTAINER_SList_Iterator t;
+
+ for (t = GNUNET_CONTAINER_slist_begin (rfds->handles);
+ GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&t))
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+
+ fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&t,
+ NULL);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x (0x%x) is SET in rfds\n", fh->h,
+ fh);
+ }
+ }
+#endif
+#endif
+ }
+ if (NULL != wfds)
+ {
+ nfds = GNUNET_MAX (nfds, wfds->nsds);
+#ifdef MINGW
+ handles += write_handles = GNUNET_CONTAINER_slist_count (wfds->handles);
+#endif
+ }
+ if (NULL != efds)
+ {
+ nfds = GNUNET_MAX (nfds, efds->nsds);
+#ifdef MINGW
+ handles += ex_handles = GNUNET_CONTAINER_slist_count (efds->handles);
+#endif
+ }
+
+ if ((nfds == 0) &&
+ (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+#ifdef MINGW
+ && handles == 0
+#endif
+ )
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Fatal internal logic error, process hangs in `%s' (abort with CTRL-C)!\n"),
+ "select");
+ GNUNET_break (0);
+ }
+#ifndef MINGW
+ tv.tv_sec = timeout.rel_value / GNUNET_TIME_UNIT_SECONDS.rel_value;
+ tv.tv_usec =
+ 1000 * (timeout.rel_value -
+ (tv.tv_sec * GNUNET_TIME_UNIT_SECONDS.rel_value));
+ return select (nfds, (rfds != NULL) ? &rfds->sds : NULL,
+ (wfds != NULL) ? &wfds->sds : NULL,
+ (efds != NULL) ? &efds->sds : NULL,
+ (timeout.rel_value ==
+ GNUNET_TIME_UNIT_FOREVER_REL.rel_value) ? NULL : &tv);
+
+#else
+#define SAFE_FD_ISSET(fd, set) (set != NULL && FD_ISSET(fd, set))
+ /* calculate how long we need to wait in milliseconds */
+ if (timeout.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+ ms_total = INFINITE;
+ else
+ ms_total = timeout.rel_value / GNUNET_TIME_UNIT_MILLISECONDS.rel_value;
+ /* select() may be used as a portable way to sleep */
+ if (!(rfds || wfds || efds))
+ {
+ Sleep (ms_total);
+ return 0;
+ }
+
+ /* Events for sockets */
+ if (!hEventRead)
+ hEventRead = CreateEvent (NULL, TRUE, FALSE, NULL);
+ else
+ ResetEvent (hEventRead);
+ if (!hEventReadReady)
+ hEventReadReady = CreateEvent (NULL, TRUE, TRUE, NULL);
+ if (!hEventWrite)
+ hEventWrite = CreateEvent (NULL, TRUE, FALSE, NULL);
+ else
+ ResetEvent (hEventWrite);
+ if (!hEventException)
+ hEventException = CreateEvent (NULL, TRUE, FALSE, NULL);
+ else
+ ResetEvent (hEventException);
+
+ /* Event for pipes */
+ if (!hEventPipeWrite)
+ hEventPipeWrite = CreateEvent (NULL, TRUE, TRUE, NULL);
+ readPipes = 0;
+ writePipePos = -1;
+
+ handles_read = GNUNET_CONTAINER_slist_create ();
+ handles_write = GNUNET_CONTAINER_slist_create ();
+ handles_except = GNUNET_CONTAINER_slist_create ();
+ FD_ZERO (&aread);
+ FD_ZERO (&awrite);
+ FD_ZERO (&aexcept);
+#if DEBUG_NETWORK
+ FD_ZERO (&bread);
+ FD_ZERO (&bwrite);
+ FD_ZERO (&bexcept);
+#endif
+ if (rfds)
+ {
+ FD_COPY (&rfds->sds, &aread);
+#if DEBUG_NETWORK
+ FD_COPY (&rfds->sds, &bread);
+#endif
+ }
+ if (wfds)
+ {
+ FD_COPY (&wfds->sds, &awrite);
+#if DEBUG_NETWORK
+ FD_COPY (&wfds->sds, &bwrite);
+#endif
+ }
+ if (efds)
+ {
+ FD_COPY (&efds->sds, &aexcept);
+#if DEBUG_NETWORK
+ FD_COPY (&efds->sds, &bexcept);
+#endif
+ }
+ /* We will first Add the PIPES to the events */
+ /* Read Pipes */
+ if (rfds && read_handles)
+ {
+ struct GNUNET_CONTAINER_SList_Iterator i;
+
+ for (i = GNUNET_CONTAINER_slist_begin (rfds->handles);
+ GNUNET_CONTAINER_slist_end (&i) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&i))
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+
+ fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&i,
+ NULL);
+ if (fh->type == GNUNET_PIPE)
+ {
+ /* Read zero bytes to check the status of the pipe */
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Reading 0 bytes from the pipe 0x%x\n",
+ fh->h);
+ if (!ReadFile (fh->h, NULL, 0, NULL, fh->oOverlapRead))
+ {
+ DWORD error_code = GetLastError ();
+
+ if (error_code == ERROR_IO_PENDING)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding the pipe's 0x%x overlapped event to the array as %d\n",
+ fh->h, nhandles);
+ handle_array[nhandles++] = fh->oOverlapRead->hEvent;
+ readArray[readPipes++] = fh;
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Read failed, adding the read ready event to the array as %d\n", nhandles);
+ handle_array[nhandles++] = hEventReadReady;
+ readArray[readPipes++] = fh;
+ }
+ }
+ else
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding the read ready event to the array as %d\n", nhandles);
+ handle_array[nhandles++] = hEventReadReady;
+ readArray[readPipes++] = fh;
+ }
+ }
+ else
+ {
+ GNUNET_CONTAINER_slist_add (handles_read,
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ fh, sizeof (struct GNUNET_DISK_FileHandle));
+ }
+ }
+ }
+ if (wfds && write_handles)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding the write ready event to the array as %d\n", nhandles);
+ handle_array[nhandles++] = hEventPipeWrite;
+ writePipePos = nhandles;
+ }
+ if (efds && ex_handles)
+ {
+ struct GNUNET_CONTAINER_SList_Iterator i;
+
+ for (i = GNUNET_CONTAINER_slist_begin (efds->handles);
+ GNUNET_CONTAINER_slist_end (&i) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&i))
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+ DWORD dwBytes;
+
+ fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&i,
+ NULL);
+ if (fh->type == GNUNET_PIPE)
+ {
+ if (!PeekNamedPipe (fh->h, NULL, 0, NULL, &dwBytes, NULL))
+ {
+ GNUNET_CONTAINER_slist_add (handles_except,
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ fh,
+ sizeof (struct GNUNET_DISK_FileHandle));
+ newretcode++;
+ }
+ }
+ }
+ }
+ if (nfds > 0)
+ {
+ if (rfds)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding the socket read event to the array as %d\n", nhandles);
+ handle_array[nhandles++] = hEventRead;
+ nSockEvents++;
+ for (i = 0; i < rfds->sds.fd_count; i++)
+ {
+ WSAEventSelect (rfds->sds.fd_array[i], hEventRead,
+ FD_ACCEPT | FD_READ | FD_CLOSE);
+ nsock++;
+ }
+ }
+ if (wfds)
+ {
+ int wakeup = 0;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding the socket write event to the array as %d\n", nhandles);
+ handle_array[nhandles++] = hEventWrite;
+ nSockEvents++;
+ for (i = 0; i < wfds->sds.fd_count; i++)
+ {
+ DWORD error;
+ int status;
+
+ status = send (wfds->sds.fd_array[i], NULL, 0, 0);
+ error = GetLastError ();
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "pre-send to the socket %d returned %d (%u)\n", i, status, error);
+ if (status == 0 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN))
+ wakeup = 1;
+ WSAEventSelect (wfds->sds.fd_array[i], hEventWrite,
+ FD_WRITE | FD_CONNECT | FD_CLOSE);
+ nsock++;
+ }
+ if (wakeup)
+ SetEvent (hEventWrite);
+ }
+ if (efds)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding the socket error event to the array as %d\n", nhandles);
+ handle_array[nhandles++] = hEventException;
+ nSockEvents++;
+ for (i = 0; i < efds->sds.fd_count; i++)
+ {
+ WSAEventSelect (efds->sds.fd_array[i], hEventException,
+ FD_OOB | FD_CLOSE);
+ nsock++;
+ }
+ }
+ }
+
+ handle_array[nhandles] = NULL;
+
+#if DEBUG_NETWORK
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Number nfds : %d\n", nfds);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Number of handles : %d\n", nhandles);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "retcode : %d\n", newretcode);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Will wait : %d\n", ms_total);
+#endif
+
+ if (nhandles)
+ returncode =
+ WaitForMultipleObjects (nhandles, handle_array, FALSE, ms_total);
+#if DEBUG_NETWORK
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "WaitForMultipleObjects Returned : %d\n",
+ returncode);
+#endif
+
+ returnedpos = returncode - WAIT_OBJECT_0;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "return pos is : %d\n", returnedpos);
+
+ /* FIXME: THIS LINE IS WRONG !! We should add to handles only handles that fired the events, not all ! */
+ /*
+ * if(rfds)
+ * GNUNET_CONTAINER_slist_append (handles_read, rfds->handles);
+ */
+ if (nhandles && (returnedpos < nhandles))
+ {
+ DWORD waitstatus;
+
+ /* Do the select */
+ if (nfds)
+ {
+ struct timeval tvslice;
+
+ tvslice.tv_sec = 0;
+ tvslice.tv_usec = 10;
+ retcode = select (nfds, &aread, &awrite, &aexcept, &tvslice);
+ if (retcode == -1)
+ retcode = 0;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Select retcode : %d\n", retcode);
+ }
+ /* FIXME: <= writePipePos? Really? */
+ if ((writePipePos != -1) && (returnedpos <= writePipePos))
+ {
+ GNUNET_CONTAINER_slist_append (handles_write, wfds->handles);
+ retcode += write_handles;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Added write pipe\n");
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "ReadPipes is : %d\n", readPipes);
+ /* We have some pipes ready for read. */
+ /* FIXME: it is supposed to work !! Only choose the Pipes who fired the event, but it is not working */
+
+ if (returnedpos < readPipes)
+ {
+ /*
+ * for (i = 0; i < readPipes; i++)
+ * {
+ * waitstatus = WaitForSingleObject (handle_array[i], 0);
+ * LOG (GNUNET_ERROR_TYPE_DEBUG, "Read pipe %d wait status is : %d\n", i, waitstatus);
+ * if (waitstatus != WAIT_OBJECT_0)
+ * continue;
+ * GNUNET_CONTAINER_slist_add (handles_read,
+ * GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ * readArray[i], sizeof (struct GNUNET_DISK_FileHandle));
+ * retcode++;
+ * LOG (GNUNET_ERROR_TYPE_DEBUG, "Added read Pipe\n");
+ * }
+ */
+ for (i = 0; i < readPipes; i++)
+ {
+ DWORD error;
+ BOOL bret;
+
+ SetLastError (0);
+ waitstatus = 0;
+ bret =
+ PeekNamedPipe (readArray[i]->h, NULL, 0, NULL, &waitstatus, NULL);
+ error = GetLastError ();
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Peek at read pipe %d (0x%x) returned %d (%d bytes available) GLE %u\n",
+ i, readArray[i]->h, bret, waitstatus, error);
+ if (bret == 0)
+ {
+ if (error != ERROR_BROKEN_PIPE)
+ continue;
+ }
+ else if (waitstatus <= 0)
+ continue;
+ GNUNET_CONTAINER_slist_add (handles_read,
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ readArray[i],
+ sizeof (struct GNUNET_DISK_FileHandle));
+ retcode++;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Added read Pipe 0x%x (0x%x)\n",
+ readArray[i], readArray[i]->h);
+ }
+ }
+ waitstatus = WaitForSingleObject (hEventWrite, 0);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Wait for the write event returned %d\n",
+ waitstatus);
+ if (waitstatus == WAIT_OBJECT_0)
+ {
+ for (i = 0; i < wfds->sds.fd_count; i++)
+ {
+ DWORD error;
+ int status;
+ int so_error = 0;
+ int sizeof_so_error = sizeof (so_error);
+ int gso_result =
+ getsockopt (wfds->sds.fd_array[i], SOL_SOCKET, SO_ERROR,
+ (char *) &so_error, &sizeof_so_error);
+
+ status = send (wfds->sds.fd_array[i], NULL, 0, 0);
+ error = GetLastError ();
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "send to the socket %d returned %d (%u)\n", i, status, error);
+ if (status == 0 || (error != WSAEWOULDBLOCK && error != WSAENOTCONN) ||
+ (status == -1 && gso_result == 0 && error == WSAENOTCONN &&
+ so_error == WSAECONNREFUSED))
+ {
+ FD_SET (wfds->sds.fd_array[i], &awrite);
+ retcode += 1;
+ }
+ }
+ }
+ }
+#if DEBUG_NETWORK
+ if (!nhandles || (returnedpos >= nhandles))
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Returning from _select() with nothing!\n");
+#endif
+ if (rfds)
+ {
+ struct GNUNET_CONTAINER_SList_Iterator t;
+
+ for (i = 0; i < rfds->sds.fd_count; i++)
+ {
+ WSAEventSelect (rfds->sds.fd_array[i], hEventRead, 0);
+ nsock++;
+ }
+ for (t = GNUNET_CONTAINER_slist_begin (rfds->handles);
+ GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&t))
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+
+ fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&t,
+ NULL);
+ if (fh->type == GNUNET_PIPE)
+ {
+ CancelIo (fh->h);
+ }
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Zeroing rfds\n");
+ GNUNET_NETWORK_fdset_zero (rfds);
+ if (retcode != -1 && nhandles && (returnedpos < nhandles))
+ GNUNET_NETWORK_fdset_copy_native (rfds, &aread, retcode);
+ GNUNET_CONTAINER_slist_append (rfds->handles, handles_read);
+ }
+ if (wfds)
+ {
+ for (i = 0; i < wfds->sds.fd_count; i++)
+ {
+ WSAEventSelect (wfds->sds.fd_array[i], hEventWrite, 0);
+ nsock++;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Zeroing wfds\n");
+ GNUNET_NETWORK_fdset_zero (wfds);
+ if (retcode != -1 && nhandles && (returnedpos < nhandles))
+ GNUNET_NETWORK_fdset_copy_native (wfds, &awrite, retcode);
+ GNUNET_CONTAINER_slist_append (wfds->handles, handles_write);
+ }
+ if (efds)
+ {
+ for (i = 0; i < efds->sds.fd_count; i++)
+ {
+ WSAEventSelect (efds->sds.fd_array[i], hEventException, 0);
+ nsock++;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Zeroing efds\n");
+ GNUNET_NETWORK_fdset_zero (efds);
+ if (retcode != -1 && nhandles && (returnedpos < nhandles))
+ GNUNET_NETWORK_fdset_copy_native (efds, &aexcept, retcode);
+ GNUNET_CONTAINER_slist_append (efds->handles, handles_except);
+ }
+ GNUNET_CONTAINER_slist_destroy (handles_read);
+ GNUNET_CONTAINER_slist_destroy (handles_write);
+ GNUNET_CONTAINER_slist_destroy (handles_except);
+#if DEBUG_NETWORK
+ if (rfds)
+ {
+ struct GNUNET_CONTAINER_SList_Iterator t;
+
+ for (i = 0; i < bread.fd_count; i++)
+ {
+ if (bread.fd_array[i] != 0)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is %s in rfds\n",
+ bread.fd_array[i],
+ (SAFE_FD_ISSET (bread.fd_array[i], rfds)) ? "SET" : "NOT SET");
+ }
+ for (t = GNUNET_CONTAINER_slist_begin (rfds->handles);
+ GNUNET_CONTAINER_slist_end (&t) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&t))
+ {
+ struct GNUNET_DISK_FileHandle *fh;
+
+ fh = (struct GNUNET_DISK_FileHandle *) GNUNET_CONTAINER_slist_get (&t,
+ NULL);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is SET in rfds\n", fh->h);
+ }
+ }
+ if (wfds)
+ {
+ for (i = 0; i < bwrite.fd_count; i++)
+ {
+ if (bwrite.fd_array[i] != 0)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is %s in wfds\n",
+ bwrite.fd_array[i],
+ (SAFE_FD_ISSET (bwrite.fd_array[i], rfds)) ? "SET" : "NOT SET");
+ }
+ }
+ if (efds)
+ {
+ for (i = 0; i < bexcept.fd_count; i++)
+ {
+ if (bexcept.fd_array[i] != 0)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "FD 0x%x is %s in efds\n",
+ bexcept.fd_array[i],
+ (SAFE_FD_ISSET (bexcept.fd_array[i], rfds)) ? "SET" : "NOT SET");
+ }
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Returning %d or 0\n", retcode);
+#endif
+ if (nhandles && (returnedpos < nhandles))
+ return retcode;
+ else
+#endif
+ return 0;
+}
+
+/* end of network.c */
diff --git a/src/util/os_installation.c b/src/util/os_installation.c
new file mode 100644
index 0000000..b82813d
--- /dev/null
+++ b/src/util/os_installation.c
@@ -0,0 +1,538 @@
+/*
+ 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
+ */
+#include <sys/stat.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_configuration_lib.h"
+#include "gnunet_disk_lib.h"
+#include "gnunet_os_lib.h"
+#if DARWIN
+#include <mach-o/ldsyms.h>
+#include <mach-o/dyld.h>
+#endif
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+#if LINUX
+/**
+ * Try to determine path by reading /proc/PID/exe
+ */
+static char *
+get_path_from_proc_maps ()
+{
+ char fn[64];
+ char line[1024];
+ char dir[1024];
+ FILE *f;
+ char *lgu;
+
+ GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/maps", getpid ());
+ f = FOPEN (fn, "r");
+ if (f == NULL)
+ return NULL;
+ while (NULL != fgets (line, sizeof (line), f))
+ {
+ if ((1 ==
+ sscanf (line, "%*x-%*x %*c%*c%*c%*c %*x %*2u:%*2u %*u%*[ ]%s", dir)) &&
+ (NULL != (lgu = strstr (dir, "libgnunetutil"))))
+ {
+ lgu[0] = '\0';
+ FCLOSE (f);
+ return GNUNET_strdup (dir);
+ }
+ }
+ FCLOSE (f);
+ return NULL;
+}
+
+/**
+ * Try to determine path by reading /proc/PID/exe
+ */
+static char *
+get_path_from_proc_exe ()
+{
+ char fn[64];
+ char lnk[1024];
+ ssize_t size;
+
+ GNUNET_snprintf (fn, sizeof (fn), "/proc/%u/exe", getpid ());
+ size = readlink (fn, lnk, sizeof (lnk) - 1);
+ if (size <= 0)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "readlink", fn);
+ return NULL;
+ }
+ GNUNET_assert (size < sizeof (lnk));
+ lnk[size] = '\0';
+ while ((lnk[size] != '/') && (size > 0))
+ size--;
+ if ((size < 4) || (lnk[size - 4] != '/'))
+ {
+ /* not installed in "/bin/" -- binary path probably useless */
+ return NULL;
+ }
+ lnk[size] = '\0';
+ return GNUNET_strdup (lnk);
+}
+#endif
+
+#if WINDOWS
+/**
+ * Try to determine path with win32-specific function
+ */
+static char *
+get_path_from_module_filename ()
+{
+ wchar_t path[4097];
+ char upath[4097];
+ wchar_t *idx;
+
+ GetModuleFileNameW (NULL, path, sizeof (path) - 1);
+ idx = path + wcslen (path);
+ while ((idx > path) && (*idx != L'\\') && (*idx != L'/'))
+ idx--;
+ *idx = L'\0';
+ upath[0] = '\0';
+ WideCharToMultiByte (CP_UTF8, 0, path, -1, upath, 4097, NULL, NULL);
+
+ return GNUNET_strdup (upath);
+}
+#endif
+
+#if DARWIN
+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 = GNUNET_strdup (path);
+ s = p + strlen (p);
+ while ((s > p) && (*s != '/'))
+ s--;
+ s++;
+ *s = '\0';
+ }
+ break;
+ }
+ }
+ return p;
+}
+#endif
+
+/**
+ * Return the actual path to a file found in the current
+ * PATH environment variable.
+ *
+ * @param binary the name of the file to find
+ * @return path to binary, NULL if not found
+ */
+static char *
+get_path_from_PATH (const char *binary)
+{
+ char *path;
+ char *pos;
+ char *end;
+ char *buf;
+ const char *p;
+
+ p = getenv ("PATH");
+ if (p == NULL)
+ return NULL;
+ path = GNUNET_strdup (p); /* because we write on it */
+ buf = GNUNET_malloc (strlen (path) + 20);
+ pos = path;
+ while (NULL != (end = strchr (pos, PATH_SEPARATOR)))
+ {
+ *end = '\0';
+ sprintf (buf, "%s/%s", pos, binary);
+ 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, binary);
+ 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 DARWIN
+ 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 ("gnunet-arm");
+ if (ret != NULL)
+ return ret;
+ /* other attempts here */
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Could not determine installation path for %s. Set `%s' environment variable.\n"),
+ "GNUnet", "GNUNET_PREFIX");
+ 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;
+
+ ret = NULL;
+#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 DARWIN
+ ret = get_path_from_NSGetExecutablePath ();
+ if (ret != NULL)
+ return ret;
+#endif
+ /* other attempts here */
+ return ret;
+}
+
+
+
+/**
+ * @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;
+ case GNUNET_OS_IPK_ICONDIR:
+ dirname =
+ DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "icons" DIR_SEPARATOR_STR;
+ break;
+ case GNUNET_OS_IPK_DOCDIR:
+ dirname =
+ DIR_SEPARATOR_STR "share" DIR_SEPARATOR_STR "doc" DIR_SEPARATOR_STR \
+ "gnunet" 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;
+}
+
+
+/**
+ * Check whether an executable exists and possibly
+ * if the suid bit is set on the file.
+ * Attempts to find the file using the current
+ * PATH environment variable as a search path.
+ *
+ * @param binary the name of the file to check
+ * @return GNUNET_YES if the file is SUID,
+ * GNUNET_NO if not SUID (but binary exists)
+ * GNUNET_SYSERR on error (no such binary or not executable)
+ */
+int
+GNUNET_OS_check_helper_binary (const char *binary)
+{
+ struct stat statbuf;
+ char *p;
+ char *pf;
+
+#ifdef MINGW
+ SOCKET rawsock;
+ char *binaryexe;
+
+ GNUNET_asprintf (&binaryexe, "%s.exe", binary);
+ p = get_path_from_PATH (binaryexe);
+ if (p != NULL)
+ {
+ GNUNET_asprintf (&pf, "%s/%s", p, binaryexe);
+ GNUNET_free (p);
+ p = pf;
+ }
+ GNUNET_free (binaryexe);
+#else
+ p = get_path_from_PATH (binary);
+ if (p != NULL)
+ {
+ GNUNET_asprintf (&pf, "%s/%s", p, binary);
+ GNUNET_free (p);
+ p = pf;
+ }
+#endif
+ if (p == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Could not find binary `%s' in PATH!\n"),
+ binary);
+ return GNUNET_SYSERR;
+ }
+ if (0 != ACCESS (p, X_OK))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("access (%s, X_OK) failed: %s\n"), p,
+ STRERROR (errno));
+ GNUNET_free (p);
+ return GNUNET_SYSERR;
+ }
+#ifndef MINGW
+ if (0 == getuid ())
+ {
+ /* as we run as root, we don't insist on SUID */
+ GNUNET_free (p);
+ return GNUNET_OK;
+ }
+#endif
+ if (0 != STAT (p, &statbuf))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("stat (%s) failed: %s\n"), p,
+ STRERROR (errno));
+ GNUNET_free (p);
+ return GNUNET_SYSERR;
+ }
+#ifndef MINGW
+ if ((0 != (statbuf.st_mode & S_ISUID)) && (statbuf.st_uid == 0))
+ {
+ GNUNET_free (p);
+ return GNUNET_YES;
+ }
+ /* binary exists, but not SUID */
+ GNUNET_free (p);
+ return GNUNET_NO;
+#else
+ GNUNET_free (p);
+ rawsock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (INVALID_SOCKET == rawsock)
+ {
+ DWORD err = GetLastError ();
+
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "socket (AF_INET, SOCK_RAW, IPPROTO_ICMP) failed! GLE = %d\n", err);
+ return GNUNET_NO; /* not running as administrator */
+ }
+ closesocket (rawsock);
+ return GNUNET_YES;
+#endif
+}
+
+
+/* end of os_installation.c */
diff --git a/src/util/os_network.c b/src/util/os_network.c
new file mode 100644
index 0000000..b0490ef
--- /dev/null
+++ b/src/util/os_network.c
@@ -0,0 +1,270 @@
+/*
+ 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
+ * @author LRN
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_os_lib.h"
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * @brief Enumerate all network interfaces
+ *
+ * @param proc the callback function
+ * @param proc_cls closure for proc
+ */
+void
+GNUNET_OS_network_interfaces_list (GNUNET_OS_NetworkInterfaceProcessor proc,
+ void *proc_cls)
+{
+#ifdef MINGW
+ int r;
+ int i;
+ struct EnumNICs3_results *results = NULL;
+ int results_count;
+
+ r = EnumNICs3 (&results, &results_count);
+ if (r != GNUNET_OK)
+ return;
+
+ for (i = 0; i < results_count; i++)
+ {
+ if (GNUNET_OK !=
+ proc (proc_cls, results[i].pretty_name, results[i].is_default,
+ (const struct sockaddr *) &results[i].address,
+ results[i].
+ flags & ENUMNICS3_BCAST_OK ?
+ (const struct sockaddr *) &results[i].broadcast : NULL,
+ results[i].flags & ENUMNICS3_MASK_OK ?
+ (const struct sockaddr *) &results[i].mask : NULL,
+ results[i].addr_size))
+ break;
+ }
+ EnumNICs3_free (results);
+ return;
+
+#elif HAVE_GETIFADDRS && HAVE_FREEIFADDRS
+
+ struct ifaddrs *ifa_first;
+ struct ifaddrs *ifa_ptr;
+ socklen_t alen;
+
+ if (getifaddrs (&ifa_first) == 0)
+ {
+ 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 (proc_cls, ifa_ptr->ifa_name,
+ 0 == strcmp (ifa_ptr->ifa_name, GNUNET_DEFAULT_INTERFACE),
+ ifa_ptr->ifa_addr, ifa_ptr->ifa_broadaddr,
+ ifa_ptr->ifa_netmask, alen))
+ break;
+ }
+ }
+ freeifaddrs (ifa_first);
+ }
+#else
+ int i;
+ char line[1024];
+ char *replace;
+ const char *start;
+ char ifc[12];
+ char addrstr[128];
+ char bcstr[128];
+ char netmaskstr[128];
+ FILE *f;
+ int have_ifc;
+ struct sockaddr_in a4;
+ struct sockaddr_in6 a6;
+ struct in_addr v4;
+ struct in6_addr v6;
+ struct sockaddr_in bcaddr;
+ struct sockaddr_in netmask;
+ struct sockaddr_in6 netmask6;
+ struct sockaddr *pass_bcaddr;
+ struct sockaddr *pass_netmask;
+ int prefixlen;
+
+ 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)
+ {
+ 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 */
+
+ /* make parsing of ipv6 addresses easier */
+ for (replace = line; *replace != '\0'; replace++)
+ {
+ if (*replace == '/')
+ *replace = ' ';
+ }
+ prefixlen = -1;
+
+ start = line;
+ while (('\0' != *start) && (isspace (*start)))
+ start++;
+
+ /* Zero-out stack allocated values */
+ memset (addrstr, 0, 128);
+ memset (netmaskstr, 0, 128);
+ memset (bcstr, 0, 128);
+ prefixlen = 0;
+
+ if ( /* Linux */
+ (3 == SSCANF (start, "inet addr:%127s Bcast:%127s Mask:%127s", addrstr, bcstr, netmaskstr)) ||
+ (2 == SSCANF (start, "inet addr:%127s Mask:%127s", addrstr, netmaskstr)) ||
+ (2 == SSCANF (start, "inet6 addr:%127s %d", addrstr, &prefixlen)) ||
+ /* 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;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ a4.sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+ a4.sin_addr = v4;
+
+ pass_bcaddr = NULL;
+ pass_netmask = NULL;
+ if (1 == inet_pton (AF_INET, bcstr, &v4))
+ {
+ memset (&bcaddr, 0, sizeof (bcaddr));
+ bcaddr.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ bcaddr.sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+ bcaddr.sin_addr = v4;
+ pass_bcaddr = (struct sockaddr *) &bcaddr;
+ }
+ if (1 == inet_pton (AF_INET, netmaskstr, &v4))
+ {
+ memset (&netmask, 0, sizeof (netmask));
+ netmask.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ netmask.sin_len = (u_char) sizeof (struct sockaddr_in);
+#endif
+ netmask.sin_addr = v4;
+ pass_netmask = (struct sockaddr *) &netmask;
+ }
+
+
+ if (GNUNET_OK !=
+ proc (proc_cls, ifc, 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE),
+ (const struct sockaddr *) &a4,
+ pass_bcaddr, pass_netmask, sizeof (a4)))
+ break;
+ continue;
+ }
+ /* IPv6 */
+ if (1 == inet_pton (AF_INET6, addrstr, &v6))
+ {
+ memset (&a6, 0, sizeof (a6));
+ a6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ a6.sin6_len = (u_char) sizeof (struct sockaddr_in6);
+#endif
+ a6.sin6_addr = v6;
+
+ pass_netmask = NULL;
+ if (prefixlen != -1)
+ {
+ memset (v6.s6_addr, 0, sizeof (v6.s6_addr));
+ for (i = 0; i < prefixlen; i++)
+ {
+ v6.s6_addr[i >> 3] |= 1 << (i & 7);
+ }
+ memset (&netmask6, 0, sizeof (netmask6));
+ netmask6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ netmask6.sin6_len = (u_char) sizeof (struct sockaddr_in6);
+#endif
+ netmask6.sin6_addr = v6;
+
+ pass_netmask = (struct sockaddr *) &netmask6;
+ }
+
+ if (GNUNET_OK !=
+ proc (proc_cls, ifc, 0 == strcmp (ifc, GNUNET_DEFAULT_INTERFACE),
+ (const struct sockaddr *) &a6,
+ NULL, pass_netmask, 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 0000000..a1f173a
--- /dev/null
+++ b/src/util/os_priority.c
@@ -0,0 +1,1826 @@
+/*
+ This file is part of GNUnet
+ (C) 2002, 2003, 2004, 2005, 2006, 2011 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"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_strings_lib.h"
+#include "gnunet_crypto_lib.h"
+#include "disk.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+#define GNUNET_OS_CONTROL_PIPE "GNUNET_OS_CONTROL_PIPE"
+
+struct GNUNET_OS_Process
+{
+ /**
+ * PID of the process.
+ */
+ pid_t pid;
+
+#if WINDOWS
+ /**
+ * Process handle.
+ */
+ HANDLE handle;
+#endif
+
+ /**
+ * Pipe we use to signal the process (if used).
+ */
+ struct GNUNET_DISK_FileHandle *control_pipe;
+
+ /**
+ * Name of the pipe, NULL for none.
+ */
+ char *childpipename;
+};
+
+
+/**
+ * Handle for 'this' process.
+ */
+static struct GNUNET_OS_Process current_process;
+
+
+/* MinGW version of named pipe API */
+#ifdef MINGW
+/**
+ * Creates a named pipe/FIFO and opens it
+ *
+ * @param fn pointer to the name of the named pipe or to NULL
+ * @param flags open flags
+ * @param perm access permissions
+ * @return pipe handle on success, NULL on error
+ */
+static struct GNUNET_DISK_FileHandle *
+npipe_create (char **fn, enum GNUNET_DISK_OpenFlags flags,
+ enum GNUNET_DISK_AccessPermissions perm)
+{
+ struct GNUNET_DISK_FileHandle *ret;
+ HANDLE h = NULL;
+ DWORD openMode;
+ char *name;
+
+ openMode = 0;
+ if (flags & GNUNET_DISK_OPEN_READWRITE)
+ openMode = PIPE_ACCESS_DUPLEX;
+ else if (flags & GNUNET_DISK_OPEN_READ)
+ openMode = PIPE_ACCESS_INBOUND;
+ else if (flags & GNUNET_DISK_OPEN_WRITE)
+ openMode = PIPE_ACCESS_OUTBOUND;
+ if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS)
+ openMode |= FILE_FLAG_FIRST_PIPE_INSTANCE;
+
+ while (h == NULL)
+ {
+ DWORD error_code;
+
+ name = NULL;
+ if (*fn != NULL)
+ {
+ GNUNET_asprintf (&name, "\\\\.\\pipe\\%.246s", fn);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to create an instance of named pipe `%s'\n", name);
+ /* 1) This might work just fine with UTF-8 strings as it is.
+ * 2) This is only used by GNUnet itself, and only with latin names.
+ */
+ h = CreateNamedPipe (name, openMode | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
+ NULL);
+ }
+ else
+ {
+ GNUNET_asprintf (fn, "\\\\.\\pipe\\gnunet-%llu",
+ GNUNET_CRYPTO_random_u64 (GNUNET_CRYPTO_QUALITY_WEAK,
+ UINT64_MAX));
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to create unique named pipe `%s'\n",
+ *fn);
+ h = CreateNamedPipe (*fn,
+ openMode | FILE_FLAG_OVERLAPPED |
+ FILE_FLAG_FIRST_PIPE_INSTANCE,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, 2, 1, 1, 0,
+ NULL);
+ }
+ error_code = GetLastError ();
+ if (name)
+ GNUNET_free (name);
+ /* don't re-set name to NULL yet */
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ SetErrnoFromWinError (error_code);
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Pipe creation have failed because of %d, errno is %d\n", error_code,
+ errno);
+ if (name == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Pipe was to be unique, considering re-creation\n");
+ GNUNET_free (*fn);
+ *fn = NULL;
+ if (error_code != ERROR_ACCESS_DENIED && error_code != ERROR_PIPE_BUSY)
+ {
+ return NULL;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Pipe name was not unique, trying again\n");
+ h = NULL;
+ }
+ else
+ return NULL;
+ }
+ }
+ errno = 0;
+
+ ret = GNUNET_malloc (sizeof (*ret));
+ ret->h = h;
+ ret->type = GNUNET_PIPE;
+ ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
+ ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
+ ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ return ret;
+}
+
+
+/**
+ * Opens already existing named pipe/FIFO
+ *
+ * @param fn name of an existing named pipe
+ * @param flags open flags
+ * @return pipe handle on success, NULL on error
+ */
+static struct GNUNET_DISK_FileHandle *
+npipe_open (const char *fn, enum GNUNET_DISK_OpenFlags flags)
+{
+ struct GNUNET_DISK_FileHandle *ret;
+ HANDLE h;
+ DWORD openMode;
+
+ openMode = 0;
+ if (flags & GNUNET_DISK_OPEN_READWRITE)
+ openMode = GENERIC_WRITE | GENERIC_READ;
+ else if (flags & GNUNET_DISK_OPEN_READ)
+ openMode = GENERIC_READ;
+ else if (flags & GNUNET_DISK_OPEN_WRITE)
+ openMode = GENERIC_WRITE;
+
+ h = CreateFile (fn, openMode, 0, NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED | FILE_READ_ATTRIBUTES, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ SetErrnoFromWinError (GetLastError ());
+ return NULL;
+ }
+
+ ret = GNUNET_malloc (sizeof (*ret));
+ ret->h = h;
+ ret->type = GNUNET_PIPE;
+ ret->oOverlapRead = GNUNET_malloc (sizeof (OVERLAPPED));
+ ret->oOverlapWrite = GNUNET_malloc (sizeof (OVERLAPPED));
+ ret->oOverlapRead->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+ ret->oOverlapWrite->hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
+
+ return ret;
+}
+
+#else
+/* UNIX version of named-pipe API */
+
+/**
+ * Clean up a named pipe and the directory it was placed in.
+ *
+ * @param fn name of the pipe
+ */
+static void
+cleanup_npipe (const char *fn)
+{
+ char *dn;
+ char *dp;
+
+ if (0 != unlink (fn))
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", fn);
+ dn = GNUNET_strdup (fn);
+ dp = dirname (dn);
+ if (0 != rmdir (dp))
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "rmdir", dp);
+ GNUNET_free (dn);
+}
+
+
+/**
+ * Setup a named pipe.
+ *
+ * @param fn where to store the name of the new pipe,
+ * if *fn is non-null, the name of the pipe to setup
+ * @return GNUNET_OK on success
+ */
+static int
+npipe_setup (char **fn)
+{
+ if (NULL == *fn)
+ {
+ /* FIXME: hardwired '/tmp' path... is bad */
+ char dir[] = "/tmp/gnunet-pipe-XXXXXX";
+
+ if (NULL == mkdtemp (dir))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "mkdtemp");
+ return GNUNET_SYSERR;
+ }
+ GNUNET_asprintf (fn, "%s/child-control", dir);
+ }
+ if (-1 == mkfifo (*fn, S_IRUSR | S_IWUSR))
+ return GNUNET_SYSERR;
+ return GNUNET_OK;
+}
+
+
+/**
+ * Open an existing named pipe.
+ *
+ * @param fn name of the file
+ * @param flags flags to use
+ * @return NULL on error
+ */
+static struct GNUNET_DISK_FileHandle *
+npipe_open (const char *fn,
+ enum GNUNET_DISK_OpenFlags flags)
+{
+ struct GNUNET_DISK_FileHandle *ret;
+ int fd;
+ struct timespec req;
+ int i;
+
+ /* 200 * 5ms = 1s at most */
+ for (i=0;i<200;i++)
+ {
+ fd = open (fn, O_NONBLOCK | ((flags == GNUNET_DISK_OPEN_READ) ? O_RDONLY : O_WRONLY));
+ if ( (-1 != fd) || (9 == i) || (flags == GNUNET_DISK_OPEN_READ))
+ break;
+ /* as this is for killing a child process via pipe and it is conceivable that
+ the child process simply didn't finish starting yet, we do some sleeping
+ (which is obviously usually not allowed). We can't select on the FD as
+ 'open' fails, and we probably shouldn't just "ignore" the error, so wait
+ and retry a few times is likely the best method; our process API doesn't
+ support continuations, so we need to sleep directly... */
+ req.tv_sec = 0;
+ req.tv_nsec = 5000000; /* 5ms */
+ (void) nanosleep (&req, NULL);
+ }
+ if (-1 == fd)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ (flags == GNUNET_DISK_OPEN_READ)
+ ? _("Failed to open named pipe `%s' for reading: %s\n")
+ : _("Failed to open named pipe `%s' for writing: %s\n"),
+ fn,
+ STRERROR (errno));
+ return NULL;
+ }
+ ret = GNUNET_malloc (sizeof (struct GNUNET_DISK_FileHandle));
+ ret->fd = fd;
+ return ret;
+}
+#endif
+
+
+/**
+ * This handler is called when there are control data to be read on the pipe
+ *
+ * @param cls the 'struct GNUNET_DISK_FileHandle' of the control pipe
+ * @param tc scheduler context
+ */
+static void
+parent_control_handler (void *cls,
+ const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_DISK_FileHandle *control_pipe = cls;
+ int sig;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "`%s' invoked because of %d\n", __FUNCTION__,
+ tc->reason);
+ if (tc->reason &
+ (GNUNET_SCHEDULER_REASON_SHUTDOWN | GNUNET_SCHEDULER_REASON_TIMEOUT |
+ GNUNET_SCHEDULER_REASON_PREREQ_DONE))
+ {
+ GNUNET_DISK_file_close (control_pipe);
+ return;
+ }
+ if (GNUNET_DISK_file_read (control_pipe, &sig, sizeof (sig)) !=
+ sizeof (sig))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "GNUNET_DISK_file_read");
+ GNUNET_DISK_file_close (control_pipe);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Got control code %d from parent\n", sig);
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ control_pipe, &parent_control_handler,
+ control_pipe);
+ raise (sig);
+}
+
+
+/**
+ * Task that connects this process to its parent via pipe;
+ * essentially, the parent control handler will read signal numbers
+ * from the 'GNUNET_OS_CONTROL_PIPE' (as given in an environment
+ * variable) and raise those signals.
+ *
+ * @param cls closure (unused)
+ * @param tc scheduler context (unused)
+ */
+void
+GNUNET_OS_install_parent_control_handler (void *cls,
+ const struct
+ GNUNET_SCHEDULER_TaskContext *tc)
+{
+ const char *env_buf;
+ struct GNUNET_DISK_FileHandle *control_pipe;
+
+ env_buf = getenv (GNUNET_OS_CONTROL_PIPE);
+ if ( (env_buf == NULL) || (strlen (env_buf) <= 0) )
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Not installing a handler because $%s is empty\n",
+ GNUNET_OS_CONTROL_PIPE);
+ putenv ("GNUNET_OS_CONTROL_PIPE=");
+ return;
+ }
+ control_pipe =
+ npipe_open (env_buf, GNUNET_DISK_OPEN_READ);
+ if (NULL == control_pipe)
+ {
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "open", env_buf);
+ putenv ("GNUNET_OS_CONTROL_PIPE=");
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding parent control handler pipe `%s' to the scheduler\n", env_buf);
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, control_pipe,
+ &parent_control_handler, control_pipe);
+ putenv ("GNUNET_OS_CONTROL_PIPE=");
+}
+
+
+/**
+ * Get process structure for current process
+ *
+ * The pointer it returns points to static memory location and must not be
+ * deallocated/closed
+ *
+ * @return pointer to the process sturcutre for this process
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_process_current ()
+{
+#if WINDOWS
+ current_process.pid = GetCurrentProcessId ();
+ current_process.handle = GetCurrentProcess ();
+#else
+ current_process.pid = 0;
+#endif
+ return &current_process;
+}
+
+
+/**
+ * Sends a signal to the process
+ *
+ * @param proc pointer to process structure
+ * @param sig signal
+ * @return 0 on success, -1 on error
+ */
+int
+GNUNET_OS_process_kill (struct GNUNET_OS_Process *proc, int sig)
+{
+ int ret;
+
+#if !WINDOWS
+ if ( (NULL == proc->control_pipe) &&
+ (NULL != proc->childpipename) )
+ proc->control_pipe = npipe_open (proc->childpipename,
+ GNUNET_DISK_OPEN_WRITE);
+#endif
+ if (NULL == proc->control_pipe)
+ {
+#if WINDOWS
+ /* no pipe and windows? can't do this */
+ errno = EINVAL;
+ return -1;
+#else
+ return kill (proc->pid, sig);
+#endif
+ }
+ ret = GNUNET_DISK_file_write (proc->control_pipe, &sig, sizeof (sig));
+ if (ret == sizeof (sig))
+ return 0;
+ /* pipe failed, try other methods */
+ switch (sig)
+ {
+#if !WINDOWS
+ case SIGHUP:
+#endif
+ case SIGINT:
+ case SIGKILL:
+ case SIGTERM:
+#if WINDOWS && !defined(__CYGWIN__)
+ if (0 == TerminateProcess (proc->handle, 0))
+ {
+ /* FIXME: set 'errno' */
+ return -1;
+ }
+ return 0;
+#else
+ return PLIBC_KILL (proc->pid, sig);
+#endif
+ default:
+#if WINDOWS
+ errno = EINVAL;
+ return -1;
+#else
+ return kill (proc->pid, sig);
+#endif
+ }
+}
+
+/**
+ * Get the pid of the process in question
+ *
+ * @param proc the process to get the pid of
+ *
+ * @return the current process id
+ */
+pid_t
+GNUNET_OS_process_get_pid (struct GNUNET_OS_Process * proc)
+{
+ return proc->pid;
+}
+
+
+void
+GNUNET_OS_process_close (struct GNUNET_OS_Process *proc)
+{
+#if ENABLE_WINDOWS_WORKAROUNDS
+ if (proc->control_pipe)
+ GNUNET_DISK_file_close (proc->control_pipe);
+#endif
+// FIXME NILS
+#ifdef WINDOWS
+ if (proc->handle != NULL)
+ CloseHandle (proc->handle);
+#endif
+ if (NULL != proc->childpipename)
+ {
+#if !WINDOWS
+ cleanup_npipe (proc->childpipename);
+#endif
+ GNUNET_free (proc->childpipename);
+ }
+ GNUNET_free (proc);
+}
+
+// FIXME NILS
+#if WINDOWS
+#include "gnunet_signal_lib.h"
+
+extern GNUNET_SIGNAL_Handler w32_sigchld_handler;
+
+/**
+ * Make seaspider happy.
+ */
+#define DWORD_WINAPI DWORD WINAPI
+
+/**
+ * @brief Waits for a process to terminate and invokes the SIGCHLD handler
+ * @param proc pointer to process structure
+ */
+static DWORD_WINAPI
+child_wait_thread (void *arg)
+{
+ struct GNUNET_OS_Process *proc = (struct GNUNET_OS_Process *) arg;
+
+ WaitForSingleObject (proc->handle, INFINITE);
+
+ if (w32_sigchld_handler)
+ w32_sigchld_handler ();
+
+ return 0;
+}
+#endif
+
+/**
+ * Set process priority
+ *
+ * @param proc pointer to process structure
+ * @param prio priority value
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_OS_set_process_priority (struct GNUNET_OS_Process *proc,
+ enum GNUNET_SCHEDULER_Priority prio)
+{
+ int rprio;
+
+ GNUNET_assert (prio < GNUNET_SCHEDULER_PRIORITY_COUNT);
+ if (prio == GNUNET_SCHEDULER_PRIORITY_KEEP)
+ return GNUNET_OK;
+
+ /* convert to MINGW/Unix values */
+ switch (prio)
+ {
+ case GNUNET_SCHEDULER_PRIORITY_UI:
+ case GNUNET_SCHEDULER_PRIORITY_URGENT:
+#ifdef MINGW
+ rprio = HIGH_PRIORITY_CLASS;
+#else
+ rprio = 0;
+#endif
+ break;
+
+ case GNUNET_SCHEDULER_PRIORITY_HIGH:
+#ifdef MINGW
+ rprio = ABOVE_NORMAL_PRIORITY_CLASS;
+#else
+ rprio = 5;
+#endif
+ break;
+
+ case GNUNET_SCHEDULER_PRIORITY_DEFAULT:
+#ifdef MINGW
+ rprio = NORMAL_PRIORITY_CLASS;
+#else
+ rprio = 7;
+#endif
+ break;
+
+ case GNUNET_SCHEDULER_PRIORITY_BACKGROUND:
+#ifdef MINGW
+ rprio = BELOW_NORMAL_PRIORITY_CLASS;
+#else
+ rprio = 10;
+#endif
+ break;
+
+ case GNUNET_SCHEDULER_PRIORITY_IDLE:
+#ifdef MINGW
+ rprio = IDLE_PRIORITY_CLASS;
+#else
+ rprio = 19;
+#endif
+ break;
+ default:
+ GNUNET_assert (0);
+ return GNUNET_SYSERR;
+ }
+
+ /* Set process priority */
+#ifdef MINGW
+ {
+ HANDLE h = proc->handle;
+
+ GNUNET_assert (h != NULL);
+ SetPriorityClass (h, rprio);
+ }
+#elif LINUX
+ pid_t pid;
+
+ pid = proc->pid;
+ if ((0 == pid) || (pid == getpid ()))
+ {
+ int have = nice (0);
+ int delta = rprio - have;
+
+ errno = 0;
+ if ((delta != 0) && (rprio == nice (delta)) && (errno != 0))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK, "nice");
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ {
+ if (0 != setpriority (PRIO_PROCESS, pid, rprio))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING | GNUNET_ERROR_TYPE_BULK,
+ "setpriority");
+ return GNUNET_SYSERR;
+ }
+ }
+#else
+ LOG (GNUNET_ERROR_TYPE_DEBUG | GNUNET_ERROR_TYPE_BULK,
+ "Priority management not availabe for this platform\n");
+#endif
+ return GNUNET_OK;
+}
+
+#if MINGW
+static char *
+CreateCustomEnvTable (char **vars)
+{
+ char *win32_env_table, *ptr, **var_ptr, *result, *result_ptr;
+ size_t tablesize = 0;
+ size_t items_count = 0;
+ size_t n_found = 0, n_var;
+ char *index = NULL;
+ size_t c;
+ size_t var_len;
+ char *var;
+ char *val;
+
+ win32_env_table = GetEnvironmentStringsA ();
+ if (win32_env_table == NULL)
+ return NULL;
+ for (c = 0, var_ptr = vars; *var_ptr; var_ptr += 2, c++) ;
+ n_var = c;
+ index = GNUNET_malloc (sizeof (char *) * n_var);
+ for (c = 0; c < n_var; c++)
+ index[c] = 0;
+ for (items_count = 0, ptr = win32_env_table; ptr[0] != 0; items_count++)
+ {
+ size_t len = strlen (ptr);
+ int found = 0;
+
+ for (var_ptr = vars; *var_ptr; var_ptr++)
+ {
+ var = *var_ptr++;
+ val = *var_ptr;
+ var_len = strlen (var);
+ if (strncmp (var, ptr, var_len) == 0)
+ {
+ found = 1;
+ index[c] = 1;
+ tablesize += var_len + strlen (val) + 1;
+ break;
+ }
+ }
+ if (!found)
+ tablesize += len + 1;
+ ptr += len + 1;
+ }
+ for (n_found = 0, c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
+ {
+ var = *var_ptr++;
+ val = *var_ptr;
+ if (index[c] != 1)
+ n_found += strlen (var) + strlen (val) + 1;
+ }
+ result = GNUNET_malloc (tablesize + n_found + 1);
+ for (result_ptr = result, ptr = win32_env_table; ptr[0] != 0;)
+ {
+ size_t len = strlen (ptr);
+ int found = 0;
+
+ for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
+ {
+ var = *var_ptr++;
+ val = *var_ptr;
+ var_len = strlen (var);
+ if (strncmp (var, ptr, var_len) == 0)
+ {
+ found = 1;
+ break;
+ }
+ }
+ if (!found)
+ {
+ strcpy (result_ptr, ptr);
+ result_ptr += len + 1;
+ }
+ else
+ {
+ strcpy (result_ptr, var);
+ result_ptr += var_len;
+ strcpy (result_ptr, val);
+ result_ptr += strlen (val) + 1;
+ }
+ ptr += len + 1;
+ }
+ for (c = 0, var_ptr = vars; *var_ptr; var_ptr++, c++)
+ {
+ var = *var_ptr++;
+ val = *var_ptr;
+ var_len = strlen (var);
+ if (index[c] != 1)
+ {
+ strcpy (result_ptr, var);
+ result_ptr += var_len;
+ strcpy (result_ptr, val);
+ result_ptr += strlen (val) + 1;
+ }
+ }
+ FreeEnvironmentStrings (win32_env_table);
+ GNUNET_free (index);
+ *result_ptr = 0;
+ return result;
+}
+#endif
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param pipe_stdin pipe to use to send input to child process (or NULL)
+ * @param pipe_stdout pipe to use to get output from child process (or NULL)
+ * @param filename name of the binary
+ * @param argv NULL-terminated array of arguments to the process
+ * @return pointer to process structure of the new process, NULL on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_vap (int pipe_control,
+ struct GNUNET_DISK_PipeHandle *pipe_stdin,
+ struct GNUNET_DISK_PipeHandle *pipe_stdout,
+ const char *filename,
+ char *const argv[])
+{
+#ifndef MINGW
+ char *childpipename = NULL;
+ struct GNUNET_OS_Process *gnunet_proc = NULL;
+ pid_t ret;
+ int fd_stdout_write;
+ int fd_stdout_read;
+ int fd_stdin_read;
+ int fd_stdin_write;
+
+ if ( (GNUNET_YES == pipe_control) &&
+ (GNUNET_OK !=
+ npipe_setup (&childpipename)) )
+ return NULL;
+ if (pipe_stdout != NULL)
+ {
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (pipe_stdout,
+ GNUNET_DISK_PIPE_END_WRITE),
+ &fd_stdout_write, sizeof (int)));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (pipe_stdout, GNUNET_DISK_PIPE_END_READ),
+ &fd_stdout_read, sizeof (int)));
+ }
+ if (pipe_stdin != NULL)
+ {
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
+ &fd_stdin_read, sizeof (int)));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (pipe_stdin, GNUNET_DISK_PIPE_END_WRITE),
+ &fd_stdin_write, sizeof (int)));
+ }
+
+ ret = fork ();
+ if (-1 == ret)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
+ GNUNET_free_non_null (childpipename);
+ return NULL;
+ }
+ if (0 != ret)
+ {
+ gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
+ gnunet_proc->pid = ret;
+ gnunet_proc->childpipename = childpipename;
+ return gnunet_proc;
+ }
+ if (NULL != childpipename)
+ {
+ setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
+ GNUNET_free (childpipename);
+ }
+ if (pipe_stdout != NULL)
+ {
+ GNUNET_break (0 == close (fd_stdout_read));
+ if (-1 == dup2 (fd_stdout_write, 1))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+ GNUNET_break (0 == close (fd_stdout_write));
+ }
+
+ if (pipe_stdin != NULL)
+ {
+
+ GNUNET_break (0 == close (fd_stdin_write));
+ if (-1 == dup2 (fd_stdin_read, 0))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+ GNUNET_break (0 == close (fd_stdin_read));
+ }
+ execvp (filename, argv);
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
+ _exit (1);
+#else
+ char *childpipename = NULL;
+ struct GNUNET_OS_Process *gnunet_proc = NULL;
+ char *arg;
+ unsigned int cmdlen;
+ char *cmd, *idx;
+ STARTUPINFOW start;
+ PROCESS_INFORMATION proc;
+ int argc, arg_count;
+ HANDLE stdin_handle;
+ HANDLE stdout_handle;
+ struct GNUNET_DISK_FileHandle *control_pipe;
+
+ char path[MAX_PATH + 1];
+
+ char *our_env[3] = { NULL, NULL, NULL };
+ char *env_block = NULL;
+ char *pathbuf;
+ DWORD pathbuf_len, alloc_len;
+ char *self_prefix;
+ char *bindir;
+ char *libdir;
+ char *ptr;
+ char *non_const_filename;
+ wchar_t wpath[MAX_PATH + 1], wcmd[32768];
+
+ /* Search in prefix dir (hopefully - the directory from which
+ * the current module was loaded), bindir and libdir, then in PATH
+ */
+ self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
+ bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
+ libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+
+ pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
+
+ alloc_len =
+ pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
+ strlen (libdir);
+
+ pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
+
+ ptr = pathbuf;
+ ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
+ GNUNET_free (self_prefix);
+ GNUNET_free (bindir);
+ GNUNET_free (libdir);
+
+ alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
+ GNUNET_assert (alloc_len == (pathbuf_len - 1));
+
+ cmdlen = strlen (filename);
+ if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
+ GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
+ else
+ GNUNET_asprintf (&non_const_filename, "%s", filename);
+
+ /* Check that this is the full path. If it isn't, search. */
+ if (non_const_filename[1] == ':')
+ snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
+ else if (!SearchPathA
+ (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
+ path, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
+ non_const_filename);
+ GNUNET_free (non_const_filename);
+ GNUNET_free (pathbuf);
+ return NULL;
+ }
+ GNUNET_free (pathbuf);
+ GNUNET_free (non_const_filename);
+
+ cmdlen = 0;
+ argc = 0;
+ while (NULL != (arg = argv[argc++]))
+ {
+ if (cmdlen == 0)
+ cmdlen = cmdlen + strlen (path) + 4;
+ else
+ cmdlen = cmdlen + strlen (arg) + 4;
+ }
+ arg_count = argc;
+
+ cmd = idx = GNUNET_malloc (sizeof (char) * (cmdlen + 1));
+ argc = 0;
+ while (NULL != (arg = argv[argc++]))
+ {
+ /* This is to escape trailing slash */
+ char arg_lastchar = arg[strlen (arg) - 1];
+ if (idx == cmd)
+ idx += sprintf (idx, "\"%s%s\"%s", path,
+ arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
+ else
+ idx += sprintf (idx, "\"%s%s\"%s", arg,
+ arg_lastchar == '\\' ? "\\" : "", argc + 1 == arg_count ? "" : " ");
+ }
+
+ memset (&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+
+ if ((pipe_stdin != NULL) || (pipe_stdout != NULL))
+ start.dwFlags |= STARTF_USESTDHANDLES;
+
+ if (pipe_stdin != NULL)
+ {
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (pipe_stdin, GNUNET_DISK_PIPE_END_READ),
+ &stdin_handle, sizeof (HANDLE));
+ start.hStdInput = stdin_handle;
+ }
+
+ if (pipe_stdout != NULL)
+ {
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (pipe_stdout,
+ GNUNET_DISK_PIPE_END_WRITE),
+ &stdout_handle, sizeof (HANDLE));
+ start.hStdOutput = stdout_handle;
+ }
+ if (GNUNET_YES == pipe_control)
+ {
+ control_pipe =
+ npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ if (control_pipe == NULL)
+ {
+ GNUNET_free (cmd);
+ GNUNET_free (path);
+ return NULL;
+ }
+ }
+ if (NULL != childpipename)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
+ childpipename);
+ GNUNET_asprintf (&our_env[0], "%s=", GNUNET_OS_CONTROL_PIPE);
+ GNUNET_asprintf (&our_env[1], "%s", childpipename);
+ our_env[2] = NULL;
+ }
+ else
+ {
+ our_env[0] = NULL;
+ }
+ env_block = CreateCustomEnvTable (our_env);
+ GNUNET_free (our_env[0]);
+ GNUNET_free (our_env[1]);
+
+ if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
+ || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
+ || !CreateProcessW
+ (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
+ env_block, NULL, &start, &proc))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "CreateProcess", path);
+ GNUNET_free (env_block);
+ GNUNET_free (cmd);
+ return NULL;
+ }
+
+ GNUNET_free (env_block);
+
+ gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
+ gnunet_proc->pid = proc.dwProcessId;
+ gnunet_proc->handle = proc.hProcess;
+ gnunet_proc->control_pipe = control_pipe;
+
+ CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
+
+ ResumeThread (proc.hThread);
+ CloseHandle (proc.hThread);
+
+ GNUNET_free (cmd);
+
+ return gnunet_proc;
+#endif
+}
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param pipe_stdin pipe to use to send input to child process (or NULL)
+ * @param pipe_stdout pipe to use to get output from child process (or NULL)
+ * @param filename name of the binary
+ * @param va NULL-terminated list of arguments to the process
+ * @return pointer to process structure of the new process, NULL on error
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_va (int pipe_control,
+ struct GNUNET_DISK_PipeHandle *pipe_stdin,
+ struct GNUNET_DISK_PipeHandle *pipe_stdout,
+ const char *filename, va_list va)
+{
+ struct GNUNET_OS_Process *ret;
+ va_list ap;
+ char **argv;
+ int argc;
+
+ argc = 0;
+ va_copy (ap, va);
+ while (NULL != va_arg (ap, char *))
+ argc++;
+ va_end (ap);
+ argv = GNUNET_malloc (sizeof (char *) * (argc + 1));
+ argc = 0;
+ va_copy (ap, va);
+ while (NULL != (argv[argc] = va_arg (ap, char *)))
+ argc++;
+ va_end (ap);
+ ret = GNUNET_OS_start_process_vap (pipe_control,
+ pipe_stdin,
+ pipe_stdout,
+ filename,
+ argv);
+ GNUNET_free (argv);
+ return ret;
+}
+
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param pipe_stdin pipe to use to send input to child process (or NULL)
+ * @param pipe_stdout pipe to use to get output from child process (or NULL)
+ * @param filename name of the binary
+ * @param ... NULL-terminated list of arguments to the process
+ *
+ * @return pointer to process structure of the new process, NULL on error
+ *
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process (int pipe_control,
+ struct GNUNET_DISK_PipeHandle *pipe_stdin,
+ struct GNUNET_DISK_PipeHandle *pipe_stdout,
+ const char *filename, ...)
+{
+ struct GNUNET_OS_Process *ret;
+ va_list ap;
+
+ va_start (ap, filename);
+ ret = GNUNET_OS_start_process_va (pipe_control, pipe_stdin, pipe_stdout, filename, ap);
+ va_end (ap);
+ return ret;
+}
+
+
+/**
+ * Start a process.
+ *
+ * @param pipe_control should a pipe be used to send signals to the child?
+ * @param lsocks array of listen sockets to dup systemd-style (or NULL);
+ * must be NULL on platforms where dup is not supported
+ * @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
+ */
+struct GNUNET_OS_Process *
+GNUNET_OS_start_process_v (int pipe_control,
+ const SOCKTYPE *lsocks,
+ const char *filename,
+ char *const argv[])
+{
+#ifndef MINGW
+ pid_t ret;
+ char lpid[16];
+ char fds[16];
+ struct GNUNET_OS_Process *gnunet_proc = NULL;
+ char *childpipename = NULL;
+ int i;
+ int j;
+ int k;
+ int tgt;
+ int flags;
+ int *lscp;
+ unsigned int ls;
+
+ if ( (GNUNET_YES == pipe_control) &&
+ (GNUNET_OK != npipe_setup (&childpipename)) )
+ return NULL;
+ lscp = NULL;
+ ls = 0;
+ if (lsocks != NULL)
+ {
+ i = 0;
+ while (-1 != (k = lsocks[i++]))
+ GNUNET_array_append (lscp, ls, k);
+ GNUNET_array_append (lscp, ls, -1);
+ }
+ ret = fork ();
+ if (-1 == ret)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork");
+ GNUNET_free_non_null (childpipename);
+ GNUNET_array_grow (lscp, ls, 0);
+ return NULL;
+ }
+ if (0 != ret)
+ {
+ gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
+ gnunet_proc->pid = ret;
+ gnunet_proc->childpipename = childpipename;
+ GNUNET_array_grow (lscp, ls, 0);
+ return gnunet_proc;
+ }
+ if (NULL != childpipename)
+ {
+ setenv (GNUNET_OS_CONTROL_PIPE, childpipename, 1);
+ GNUNET_free (childpipename);
+ }
+ if (lscp != NULL)
+ {
+ /* read systemd documentation... */
+ GNUNET_snprintf (lpid, sizeof (lpid), "%u", getpid ());
+ setenv ("LISTEN_PID", lpid, 1);
+ i = 0;
+ tgt = 3;
+ while (-1 != lscp[i])
+ {
+ j = i + 1;
+ while (-1 != lscp[j])
+ {
+ if (lscp[j] == tgt)
+ {
+ /* dup away */
+ k = dup (lscp[j]);
+ GNUNET_assert (-1 != k);
+ GNUNET_assert (0 == close (lscp[j]));
+ lscp[j] = k;
+ break;
+ }
+ j++;
+ }
+ if (lscp[i] != tgt)
+ {
+ /* Bury any existing FD, no matter what; they should all be closed
+ * on exec anyway and the important onces have been dup'ed away */
+ (void) close (tgt);
+ GNUNET_assert (-1 != dup2 (lscp[i], tgt));
+ }
+ /* unset close-on-exec flag */
+ flags = fcntl (tgt, F_GETFD);
+ GNUNET_assert (flags >= 0);
+ flags &= ~FD_CLOEXEC;
+ fflush (stderr);
+ (void) fcntl (tgt, F_SETFD, flags);
+ tgt++;
+ i++;
+ }
+ GNUNET_snprintf (fds, sizeof (fds), "%u", i);
+ setenv ("LISTEN_FDS", fds, 1);
+ }
+ GNUNET_array_grow (lscp, ls, 0);
+ execvp (filename, argv);
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "execvp", filename);
+ _exit (1);
+#else
+ struct GNUNET_DISK_FileHandle *control_pipe = NULL;
+ char *childpipename = NULL;
+ char **arg, **non_const_argv;
+ unsigned int cmdlen;
+ char *cmd, *idx;
+ STARTUPINFOW start;
+ PROCESS_INFORMATION proc;
+ int argcount = 0;
+ struct GNUNET_OS_Process *gnunet_proc = NULL;
+ char path[MAX_PATH + 1];
+ char *our_env[5] = { NULL, NULL, NULL, NULL, NULL };
+ char *env_block = NULL;
+ char *pathbuf;
+ DWORD pathbuf_len, alloc_len;
+ char *self_prefix;
+ char *bindir;
+ char *libdir;
+ char *ptr;
+ char *non_const_filename;
+ struct GNUNET_DISK_PipeHandle *lsocks_pipe;
+ const struct GNUNET_DISK_FileHandle *lsocks_write_fd;
+ HANDLE lsocks_read;
+ HANDLE lsocks_write;
+ wchar_t wpath[MAX_PATH + 1], wcmd[32768];
+ int env_off;
+ int fail;
+
+ /* Search in prefix dir (hopefully - the directory from which
+ * the current module was loaded), bindir and libdir, then in PATH
+ */
+ self_prefix = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_SELF_PREFIX);
+ bindir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
+ libdir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+
+ pathbuf_len = GetEnvironmentVariableA ("PATH", (char *) &pathbuf, 0);
+
+ alloc_len =
+ pathbuf_len + 1 + strlen (self_prefix) + 1 + strlen (bindir) + 1 +
+ strlen (libdir);
+
+ pathbuf = GNUNET_malloc (alloc_len * sizeof (char));
+
+ ptr = pathbuf;
+ ptr += sprintf (pathbuf, "%s;%s;%s;", self_prefix, bindir, libdir);
+ GNUNET_free (self_prefix);
+ GNUNET_free (bindir);
+ GNUNET_free (libdir);
+
+ alloc_len = GetEnvironmentVariableA ("PATH", ptr, pathbuf_len);
+ if (alloc_len != pathbuf_len - 1)
+ {
+ GNUNET_free (pathbuf);
+ errno = ENOSYS; /* PATH changed on the fly. What kind of error is that? */
+ return NULL;
+ }
+
+ cmdlen = strlen (filename);
+ if (cmdlen < 5 || strcmp (&filename[cmdlen - 4], ".exe") != 0)
+ GNUNET_asprintf (&non_const_filename, "%s.exe", filename);
+ else
+ GNUNET_asprintf (&non_const_filename, "%s", filename);
+
+ /* Check that this is the full path. If it isn't, search. */
+ if (non_const_filename[1] == ':')
+ snprintf (path, sizeof (path) / sizeof (char), "%s", non_const_filename);
+ else if (!SearchPathA
+ (pathbuf, non_const_filename, NULL, sizeof (path) / sizeof (char),
+ path, NULL))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "SearchPath",
+ non_const_filename);
+ GNUNET_free (non_const_filename);
+ GNUNET_free (pathbuf);
+ return NULL;
+ }
+ GNUNET_free (pathbuf);
+ GNUNET_free (non_const_filename);
+
+ /* Count the number of arguments */
+ arg = (char **) argv;
+ while (*arg)
+ {
+ arg++;
+ argcount++;
+ }
+
+ /* Allocate a copy argv */
+ non_const_argv = GNUNET_malloc (sizeof (char *) * (argcount + 1));
+
+ /* Copy all argv strings */
+ argcount = 0;
+ arg = (char **) argv;
+ while (*arg)
+ {
+ if (arg == argv)
+ non_const_argv[argcount] = GNUNET_strdup (path);
+ else
+ non_const_argv[argcount] = GNUNET_strdup (*arg);
+ arg++;
+ argcount++;
+ }
+ non_const_argv[argcount] = NULL;
+
+ /* Count cmd len */
+ cmdlen = 1;
+ arg = non_const_argv;
+ while (*arg)
+ {
+ cmdlen = cmdlen + strlen (*arg) + 4;
+ arg++;
+ }
+
+ /* Allocate and create cmd */
+ cmd = idx = GNUNET_malloc (sizeof (char) * cmdlen);
+ arg = non_const_argv;
+ while (*arg)
+ {
+ char arg_last_char = (*arg)[strlen (*arg) - 1];
+ idx += sprintf (idx, "\"%s%s\"%s", *arg,
+ arg_last_char == '\\' ? "\\" : "", *(arg + 1) ? " " : "");
+ arg++;
+ }
+
+ while (argcount > 0)
+ GNUNET_free (non_const_argv[--argcount]);
+ GNUNET_free (non_const_argv);
+
+ memset (&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+
+ if (GNUNET_YES == pipe_control)
+ {
+ control_pipe =
+ npipe_create (&childpipename, GNUNET_DISK_OPEN_WRITE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ if (control_pipe == NULL)
+ {
+ GNUNET_free (cmd);
+ GNUNET_free (path);
+ return NULL;
+ }
+ }
+ if (lsocks != NULL && lsocks[0] != INVALID_SOCKET)
+ {
+ lsocks_pipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
+
+ if (lsocks_pipe == NULL)
+ {
+ GNUNET_free (cmd);
+ GNUNET_free (path);
+ GNUNET_DISK_pipe_close (lsocks_pipe);
+ return NULL;
+ }
+ lsocks_write_fd = GNUNET_DISK_pipe_handle (lsocks_pipe,
+ GNUNET_DISK_PIPE_END_WRITE);
+ GNUNET_DISK_internal_file_handle_ (lsocks_write_fd,
+ &lsocks_write, sizeof (HANDLE));
+ GNUNET_DISK_internal_file_handle_ (GNUNET_DISK_pipe_handle
+ (lsocks_pipe, GNUNET_DISK_PIPE_END_READ),
+ &lsocks_read, sizeof (HANDLE));
+ }
+
+ env_off = 0;
+ if (NULL != childpipename)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Opened the parent end of the pipe `%s'\n",
+ childpipename);
+ GNUNET_asprintf (&our_env[env_off++], "%s=", GNUNET_OS_CONTROL_PIPE);
+ GNUNET_asprintf (&our_env[env_off++], "%s", childpipename);
+ GNUNET_free (childpipename);
+ }
+ if ( (lsocks != NULL) && (lsocks[0] != INVALID_SOCKET))
+ {
+ /*This will tell the child that we're going to send lsocks over the pipe*/
+ GNUNET_asprintf (&our_env[env_off++], "%s=", "GNUNET_OS_READ_LSOCKS");
+ GNUNET_asprintf (&our_env[env_off++], "%lu", lsocks_read);
+ }
+ our_env[env_off++] = NULL;
+ env_block = CreateCustomEnvTable (our_env);
+ while (0 > env_off)
+ GNUNET_free_non_null (our_env[--env_off]);
+ if (ERROR_SUCCESS != plibc_conv_to_win_pathwconv(path, wpath)
+ || ERROR_SUCCESS != plibc_conv_to_win_pathwconv(cmd, wcmd)
+ || !CreateProcessW
+ (wpath, wcmd, NULL, NULL, TRUE, DETACHED_PROCESS | CREATE_SUSPENDED,
+ env_block, NULL, &start, &proc))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
+ if (NULL != control_pipe)
+ GNUNET_DISK_file_close (control_pipe);
+ if (NULL != lsocks)
+ GNUNET_DISK_pipe_close (lsocks_pipe);
+ GNUNET_free (env_block);
+ GNUNET_free (cmd);
+ return NULL;
+ }
+
+ GNUNET_free (env_block);
+
+ gnunet_proc = GNUNET_malloc (sizeof (struct GNUNET_OS_Process));
+ gnunet_proc->pid = proc.dwProcessId;
+ gnunet_proc->handle = proc.hProcess;
+ gnunet_proc->control_pipe = control_pipe;
+
+ CreateThread (NULL, 64000, &child_wait_thread, (void *) gnunet_proc, 0, NULL);
+
+ ResumeThread (proc.hThread);
+ CloseHandle (proc.hThread);
+ GNUNET_free (cmd);
+
+ if (lsocks == NULL || lsocks[0] == INVALID_SOCKET)
+ return gnunet_proc;
+
+ GNUNET_DISK_pipe_close_end (lsocks_pipe, GNUNET_DISK_PIPE_END_READ);
+
+ /* This is a replacement for "goto error" that doesn't use goto */
+ fail = 1;
+ do
+ {
+ int wrote;
+ uint64_t size, count, i;
+
+ /* Tell the number of sockets */
+ for (count = 0; lsocks && lsocks[count] != INVALID_SOCKET; count++);
+
+ wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
+ if (wrote != sizeof (count))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u count bytes to the child: %u\n", sizeof (count), GetLastError ());
+ break;
+ }
+ for (i = 0; lsocks && lsocks[i] != INVALID_SOCKET; i++)
+ {
+ WSAPROTOCOL_INFOA pi;
+ /* Get a socket duplication info */
+ if (SOCKET_ERROR == WSADuplicateSocketA (lsocks[i], gnunet_proc->pid, &pi))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to duplicate an socket[%llu]: %u\n", i, GetLastError ());
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "CreateProcess");
+ break;
+ }
+ /* Synchronous I/O is not nice, but we can't schedule this:
+ * lsocks will be closed/freed by the caller soon, and until
+ * the child creates a duplicate, closing a socket here will
+ * close it for good.
+ */
+ /* Send the size of the structure
+ * (the child might be built with different headers...)
+ */
+ size = sizeof (pi);
+ wrote = GNUNET_DISK_file_write (lsocks_write_fd, &size, sizeof (size));
+ if (wrote != sizeof (size))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u size[%llu] bytes to the child: %u\n", sizeof (size), i, GetLastError ());
+ break;
+ }
+ /* Finally! Send the data */
+ wrote = GNUNET_DISK_file_write (lsocks_write_fd, &pi, sizeof (pi));
+ if (wrote != sizeof (pi))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Failed to write %u socket[%llu] bytes to the child: %u\n", sizeof (pi), i, GetLastError ());
+ break;
+ }
+ }
+ /* This will block us until the child makes a final read or closes
+ * the pipe (hence no 'wrote' check), since we have to wait for it
+ * to duplicate the last socket, before we return and start closing
+ * our own copies)
+ */
+ wrote = GNUNET_DISK_file_write (lsocks_write_fd, &count, sizeof (count));
+ fail = 0;
+ }
+ while (fail);
+
+ GNUNET_DISK_file_sync (lsocks_write_fd);
+ GNUNET_DISK_pipe_close (lsocks_pipe);
+
+ if (fail)
+ {
+ /* If we can't pass on the socket(s), the child will block forever,
+ * better put it out of its misery.
+ */
+ TerminateProcess (gnunet_proc->handle, 0);
+ CloseHandle (gnunet_proc->handle);
+ if (NULL != gnunet_proc->control_pipe)
+ GNUNET_DISK_file_close (gnunet_proc->control_pipe);
+ GNUNET_free (gnunet_proc);
+ return NULL;
+ }
+ return gnunet_proc;
+#endif
+}
+
+
+/**
+ * Retrieve the status of a process
+ * @param proc process ID
+ * @param type status type
+ * @param code return code/signal number
+ * @return GNUNET_OK on success, GNUNET_NO if the process is still running, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_OS_process_status (struct GNUNET_OS_Process *proc,
+ enum GNUNET_OS_ProcessStatusType *type,
+ unsigned long *code)
+{
+#ifndef MINGW
+ int status;
+ int ret;
+
+ GNUNET_assert (0 != proc);
+ ret = waitpid (proc->pid, &status, WNOHANG);
+ if (ret < 0)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
+ return GNUNET_SYSERR;
+ }
+ if (0 == ret)
+ {
+ *type = GNUNET_OS_PROCESS_RUNNING;
+ *code = 0;
+ return GNUNET_NO;
+ }
+ if (proc->pid != ret)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
+ return GNUNET_SYSERR;
+ }
+ if (WIFEXITED (status))
+ {
+ *type = GNUNET_OS_PROCESS_EXITED;
+ *code = WEXITSTATUS (status);
+ }
+ else if (WIFSIGNALED (status))
+ {
+ *type = GNUNET_OS_PROCESS_SIGNALED;
+ *code = WTERMSIG (status);
+ }
+ else if (WIFSTOPPED (status))
+ {
+ *type = GNUNET_OS_PROCESS_SIGNALED;
+ *code = WSTOPSIG (status);
+ }
+#ifdef WIFCONTINUED
+ else if (WIFCONTINUED (status))
+ {
+ *type = GNUNET_OS_PROCESS_RUNNING;
+ *code = 0;
+ }
+#endif
+ else
+ {
+ *type = GNUNET_OS_PROCESS_UNKNOWN;
+ *code = 0;
+ }
+#else
+ HANDLE h;
+ DWORD c, error_code, ret;
+
+ h = proc->handle;
+ ret = proc->pid;
+ if (h == NULL || ret == 0)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
+ ret, h);
+ return GNUNET_SYSERR;
+ }
+ if (h == NULL)
+ h = GetCurrentProcess ();
+
+ SetLastError (0);
+ ret = GetExitCodeProcess (h, &c);
+ error_code = GetLastError ();
+ if (ret == 0 || error_code != NO_ERROR)
+ {
+ SetErrnoFromWinError (error_code);
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "GetExitCodeProcess");
+ return GNUNET_SYSERR;
+ }
+ if (STILL_ACTIVE == c)
+ {
+ *type = GNUNET_OS_PROCESS_RUNNING;
+ *code = 0;
+ return GNUNET_NO;
+ }
+ *type = GNUNET_OS_PROCESS_EXITED;
+ *code = c;
+#endif
+
+ return GNUNET_OK;
+}
+
+
+/**
+ * Wait for a process
+ * @param proc pointer to process structure
+ * @return GNUNET_OK on success, GNUNET_SYSERR otherwise
+ */
+int
+GNUNET_OS_process_wait (struct GNUNET_OS_Process *proc)
+{
+
+#ifndef MINGW
+ pid_t pid = proc->pid;
+ pid_t ret;
+
+ while ( (pid != (ret = waitpid (pid, NULL, 0))) &&
+ (EINTR == errno) ) ;
+ if (pid != ret)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "waitpid");
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+#else
+ HANDLE h;
+ int ret;
+
+ h = proc->handle;
+ if (NULL == h)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, "Invalid process information {%d, %08X}\n",
+ proc->pid, h);
+ return GNUNET_SYSERR;
+ }
+ if (h == NULL)
+ h = GetCurrentProcess ();
+
+ if (WAIT_OBJECT_0 != WaitForSingleObject (h, INFINITE))
+ {
+ SetErrnoFromWinError (GetLastError ());
+ ret = GNUNET_SYSERR;
+ }
+ else
+ ret = GNUNET_OK;
+
+ return ret;
+#endif
+}
+
+
+/**
+ * Handle to a command.
+ */
+struct GNUNET_OS_CommandHandle
+{
+
+ /**
+ * Process handle.
+ */
+ struct GNUNET_OS_Process *eip;
+
+ /**
+ * Handle to the output pipe.
+ */
+ struct GNUNET_DISK_PipeHandle *opipe;
+
+ /**
+ * Read-end of output pipe.
+ */
+ const struct GNUNET_DISK_FileHandle *r;
+
+ /**
+ * Function to call on each line of output.
+ */
+ GNUNET_OS_LineProcessor proc;
+
+ /**
+ * Closure for 'proc'.
+ */
+ void *proc_cls;
+
+ /**
+ * Buffer for the output.
+ */
+ char buf[1024];
+
+ /**
+ * Task reading from pipe.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier rtask;
+
+ /**
+ * When to time out.
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Current read offset in buf.
+ */
+ size_t off;
+};
+
+
+/**
+ * Stop/kill a command. Must ONLY be called either from
+ * the callback after 'NULL' was passed for 'line' *OR*
+ * from an independent task (not within the line processor).
+ *
+ * @param cmd handle to the process
+ */
+void
+GNUNET_OS_command_stop (struct GNUNET_OS_CommandHandle *cmd)
+{
+
+ if (cmd->proc != NULL)
+ {
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cmd->rtask);
+ GNUNET_SCHEDULER_cancel (cmd->rtask);
+ }
+ (void) GNUNET_OS_process_kill (cmd->eip, SIGKILL);
+ GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (cmd->eip));
+ GNUNET_OS_process_close (cmd->eip);
+ GNUNET_DISK_pipe_close (cmd->opipe);
+ GNUNET_free (cmd);
+}
+
+
+/**
+ * Read from the process and call the line processor.
+ *
+ * @param cls the 'struct GNUNET_OS_CommandHandle'
+ * @param tc scheduler context
+ */
+static void
+cmd_read (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_OS_CommandHandle *cmd = cls;
+ GNUNET_OS_LineProcessor proc;
+ char *end;
+ ssize_t ret;
+
+ cmd->rtask = GNUNET_SCHEDULER_NO_TASK;
+ if (GNUNET_YES != GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, cmd->r))
+ {
+ /* timeout, shutdown, etc. */
+ proc = cmd->proc;
+ cmd->proc = NULL;
+ proc (cmd->proc_cls, NULL);
+ return;
+ }
+ ret =
+ GNUNET_DISK_file_read (cmd->r, &cmd->buf[cmd->off],
+ sizeof (cmd->buf) - cmd->off);
+ if (ret <= 0)
+ {
+ if ((cmd->off > 0) && (cmd->off < sizeof (cmd->buf)))
+ {
+ cmd->buf[cmd->off] = '\0';
+ cmd->proc (cmd->proc_cls, cmd->buf);
+ }
+ proc = cmd->proc;
+ cmd->proc = NULL;
+ proc (cmd->proc_cls, NULL);
+ return;
+ }
+ end = memchr (&cmd->buf[cmd->off], '\n', ret);
+ cmd->off += ret;
+ while (end != NULL)
+ {
+ *end = '\0';
+ cmd->proc (cmd->proc_cls, cmd->buf);
+ memmove (cmd->buf, end + 1, cmd->off - (end + 1 - cmd->buf));
+ cmd->off -= (end + 1 - cmd->buf);
+ end = memchr (cmd->buf, '\n', cmd->off);
+ }
+ cmd->rtask =
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_absolute_get_remaining
+ (cmd->timeout), cmd->r, &cmd_read, cmd);
+}
+
+
+/**
+ * Run the given command line and call the given function
+ * for each line of the output.
+ *
+ * @param proc function to call for each line of the output
+ * @param proc_cls closure for proc
+ * @param timeout when to time out
+ * @param binary command to run
+ * @param ... arguments to command
+ * @return NULL on error
+ */
+struct GNUNET_OS_CommandHandle *
+GNUNET_OS_command_run (GNUNET_OS_LineProcessor proc, void *proc_cls,
+ struct GNUNET_TIME_Relative timeout, const char *binary,
+ ...)
+{
+ struct GNUNET_OS_CommandHandle *cmd;
+ struct GNUNET_OS_Process *eip;
+ struct GNUNET_DISK_PipeHandle *opipe;
+ va_list ap;
+
+ opipe = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
+ if (NULL == opipe)
+ return NULL;
+ va_start (ap, binary);
+ eip = GNUNET_OS_start_process_va (GNUNET_NO, NULL, opipe, binary, ap);
+ va_end (ap);
+ if (NULL == eip)
+ {
+ GNUNET_DISK_pipe_close (opipe);
+ return NULL;
+ }
+ GNUNET_DISK_pipe_close_end (opipe, GNUNET_DISK_PIPE_END_WRITE);
+ cmd = GNUNET_malloc (sizeof (struct GNUNET_OS_CommandHandle));
+ cmd->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ cmd->eip = eip;
+ cmd->opipe = opipe;
+ cmd->proc = proc;
+ cmd->proc_cls = proc_cls;
+ cmd->r = GNUNET_DISK_pipe_handle (opipe, GNUNET_DISK_PIPE_END_READ);
+ cmd->rtask = GNUNET_SCHEDULER_add_read_file (timeout, cmd->r, &cmd_read, cmd);
+ return cmd;
+}
+
+
+
+
+/* end of os_priority.c */
diff --git a/src/util/peer.c b/src/util/peer.c
new file mode 100644
index 0000000..2444cb9
--- /dev/null
+++ b/src/util/peer.c
@@ -0,0 +1,241 @@
+/*
+ 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., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ */
+
+/**
+ * @file util/peer.c
+ * @brief peer-ID table that assigns integer IDs to peer-IDs to save memory
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_peer_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+
+struct PeerEntry
+{
+ /**
+ * The identifier itself
+ */
+ struct GNUNET_PeerIdentity id;
+
+ /**
+ * Short version of the identifier; if the RC==0, then index of next
+ * free slot in table, otherwise equal to this slot in the table.
+ */
+ GNUNET_PEER_Id pid;
+
+ /**
+ * Reference counter, 0 if this slot is not used.
+ */
+ unsigned int rc;
+};
+
+
+/**
+ * Table with our interned peer IDs.
+ */
+static struct PeerEntry *table;
+
+/**
+ * Hashmap of PeerIdentities to "struct PeerEntry"
+ * (for fast lookup). NULL until the library
+ * is actually being used.
+ */
+static struct GNUNET_CONTAINER_MultiHashMap *map;
+
+/**
+ * Size of the "table".
+ */
+static unsigned int size;
+
+/**
+ * Index of the beginning of the free list in the table; set to "size"
+ * if no slots are free in the table.
+ */
+static unsigned int free_list_start;
+
+
+/**
+ * Search for a peer identity. The reference counter is not changed.
+ *
+ * @param pid identity to find
+ * @return the interned identity or 0.
+ */
+GNUNET_PEER_Id
+GNUNET_PEER_search (const struct GNUNET_PeerIdentity *pid)
+{
+ struct PeerEntry *e;
+ long off;
+
+ if (pid == NULL)
+ return 0;
+ if (NULL == map)
+ return 0;
+ off = (long) GNUNET_CONTAINER_multihashmap_get (map, &pid->hashPubKey);
+ e = (off == 0) ? NULL : &table[off];
+ if (e == NULL)
+ return 0;
+ GNUNET_assert (e->rc > 0);
+ return e->pid;
+}
+
+
+/**
+ * Intern an peer identity. If the identity is already known, its
+ * reference counter will be increased by one.
+ *
+ * @param pid identity to intern
+ * @return the interned identity.
+ */
+GNUNET_PEER_Id
+GNUNET_PEER_intern (const struct GNUNET_PeerIdentity *pid)
+{
+ GNUNET_PEER_Id ret;
+ struct PeerEntry *e;
+ unsigned int i;
+ long off;
+
+ if (pid == NULL)
+ return 0;
+ if (NULL == map)
+ map = GNUNET_CONTAINER_multihashmap_create (32);
+ off = (long) GNUNET_CONTAINER_multihashmap_get (map, &pid->hashPubKey);
+ e = (off == 0) ? NULL : &table[off];
+ if (e != NULL)
+ {
+ GNUNET_assert (e->rc > 0);
+ e->rc++;
+ return e->pid;
+ }
+ ret = free_list_start;
+ if (ret == size)
+ {
+ GNUNET_array_grow (table, size, size + 16);
+ for (i = ret; i < size; i++)
+ table[i].pid = i + 1;
+ }
+ if (ret == 0)
+ {
+ table[0].pid = 0;
+ table[0].rc = 1;
+ ret = 1;
+ }
+ GNUNET_assert (ret < size);
+ GNUNET_assert (table[ret].rc == 0);
+ free_list_start = table[ret].pid;
+ table[ret].id = *pid;
+ table[ret].rc = 1;
+ table[ret].pid = ret;
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_put (map, &pid->hashPubKey,
+ (void *) (long) ret,
+ GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
+ return ret;
+}
+
+
+/**
+ * Decrement multiple RCs of peer identities by one.
+ *
+ * @param ids array of PIDs to decrement the RCs of
+ * @param count size of the ids array
+ */
+void
+GNUNET_PEER_decrement_rcs (const GNUNET_PEER_Id *ids, unsigned int count)
+{
+ int i;
+ GNUNET_PEER_Id id;
+
+ if (count == 0)
+ return;
+ for (i = count - 1; i >= 0; i--)
+ {
+ id = ids[i];
+ if (id == 0)
+ continue;
+ GNUNET_assert (id < size);
+ GNUNET_assert (table[id].rc > 0);
+ table[id].rc--;
+ if (table[id].rc == 0)
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (map,
+ &table[id].
+ id.hashPubKey,
+ (void *) (long) id));
+ table[id].pid = free_list_start;
+ free_list_start = id;
+ }
+ }
+}
+
+
+/**
+ * Change the reference counter of an interned PID.
+ *
+ * @param id identity to change the RC of
+ * @param delta how much to change the RC
+ */
+void
+GNUNET_PEER_change_rc (GNUNET_PEER_Id id, int delta)
+{
+ if (id == 0)
+ return;
+ GNUNET_assert (id < size);
+ GNUNET_assert (table[id].rc > 0);
+ GNUNET_assert ((delta >= 0) || (table[id].rc >= -delta));
+ table[id].rc += delta;
+ if (table[id].rc == 0)
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONTAINER_multihashmap_remove (map,
+ &table[id].
+ id.hashPubKey,
+ (void *) (long) id));
+ table[id].pid = free_list_start;
+ free_list_start = id;
+ }
+}
+
+
+/**
+ * Convert an interned PID to a normal peer identity.
+ *
+ * @param id interned PID to convert
+ * @param pid where to write the normal peer identity
+ */
+void
+GNUNET_PEER_resolve (GNUNET_PEER_Id id, struct GNUNET_PeerIdentity *pid)
+{
+ if (id == 0)
+ {
+ memset (pid, 0, sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_break (0);
+ return;
+ }
+ GNUNET_assert (id < size);
+ GNUNET_assert (table[id].rc > 0);
+ *pid = table[id].id;
+}
+
+
+/* end of peer.c */
diff --git a/src/util/perf_crypto_hash.c b/src/util/perf_crypto_hash.c
new file mode 100644
index 0000000..d883776
--- /dev/null
+++ b/src/util/perf_crypto_hash.c
@@ -0,0 +1,70 @@
+/*
+ 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"
+#include <gauger.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",
+ (unsigned long long)
+ GNUNET_TIME_absolute_get_duration (start).rel_value);
+ GAUGER ("UTIL", "Cryptographic hashing",
+ 1024 * 64 * 1024 / (1 +
+ GNUNET_TIME_absolute_get_duration
+ (start).rel_value), "kb/s");
+ return 0;
+}
+
+/* end of hashperf.c */
diff --git a/src/util/plugin.c b/src/util/plugin.c
new file mode 100644
index 0000000..4e0385a
--- /dev/null
+++ b/src/util/plugin.c
@@ -0,0 +1,361 @@
+/*
+ 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 <ltdl.h>
+#include "gnunet_common.h"
+#include "gnunet_os_lib.h"
+#include "gnunet_plugin_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * 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;
+};
+
+
+/**
+ * Have we been initialized?
+ */
+static int initialized;
+
+
+/**
+ * Libtool search path before we started.
+ */
+static char *old_dlsearchpath;
+
+
+/**
+ * List of plugins we have loaded.
+ */
+static struct PluginList *plugins;
+
+
+/**
+ * Setup libtool paths.
+ */
+static void
+plugin_init ()
+{
+ int err;
+ const char *opath;
+ char *path;
+ char *cpath;
+
+ 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)
+ {
+ GNUNET_asprintf (&cpath, "%s:%s", opath, path);
+ lt_dlsetsearchpath (cpath);
+ GNUNET_free (path);
+ GNUNET_free (cpath);
+ }
+ else
+ {
+ lt_dlsetsearchpath (path);
+ GNUNET_free (path);
+ }
+ }
+}
+
+
+/**
+ * Shutdown libtool.
+ */
+static void
+plugin_fini ()
+{
+ lt_dlsetsearchpath (old_dlsearchpath);
+ if (old_dlsearchpath != NULL)
+ {
+ GNUNET_free (old_dlsearchpath);
+ old_dlsearchpath = NULL;
+ }
+ 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)
+ 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;
+}
+
+/**
+ * Test if a plugin exists.
+ *
+ * Note that the library must export a symbol called
+ * "library_name_init" for the test to succeed.
+ *
+ * @param library_name name of the plugin to test if it is installed
+ * @return GNUNET_YES if the plugin exists, GNUNET_NO if not
+ */
+int
+GNUNET_PLUGIN_test (const char *library_name)
+{
+ void *libhandle;
+ GNUNET_PLUGIN_Callback init;
+ struct PluginList plug;
+
+ if (!initialized)
+ {
+ initialized = GNUNET_YES;
+ plugin_init ();
+ }
+ libhandle = lt_dlopenext (library_name);
+ if (libhandle == NULL)
+ return GNUNET_NO;
+ plug.handle = libhandle;
+ plug.name = (char *) library_name;
+ init = resolve_function (&plug, "init");
+ if (init == NULL)
+ {
+ GNUNET_break (0);
+ lt_dlclose (libhandle);
+ return GNUNET_NO;
+ }
+ lt_dlclose (libhandle);
+ return GNUNET_YES;
+}
+
+
+/**
+ * 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;
+
+ if (!initialized)
+ {
+ initialized = GNUNET_YES;
+ plugin_init ();
+ }
+ libhandle = lt_dlopenext (library_name);
+ if (libhandle == NULL)
+ {
+ 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))))
+ {
+ lt_dlclose (libhandle);
+ 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);
+ if (plugins == NULL)
+ {
+ plugin_fini ();
+ initialized = GNUNET_NO;
+ }
+ return ret;
+}
+
+
+struct LoadAllContext
+{
+ const char *basename;
+ void *arg;
+ GNUNET_PLUGIN_LoaderCallback cb;
+ void *cb_cls;
+};
+
+
+static int
+find_libraries (void *cls, const char *filename)
+{
+ struct LoadAllContext *lac = cls;
+ const char *slashpos;
+ const char *libname;
+ char *basename;
+ char *dot;
+ void *lib_ret;
+ size_t n;
+
+ libname = filename;
+ while (NULL != (slashpos = strstr (libname, DIR_SEPARATOR_STR)))
+ libname = slashpos + 1;
+ n = strlen (libname);
+ if (0 != strncmp (lac->basename, libname, strlen (lac->basename)))
+ return GNUNET_OK; /* wrong name */
+ if ((n > 3) && (0 == strcmp (&libname[n - 3], ".la")))
+ return GNUNET_OK; /* .la file */
+ basename = GNUNET_strdup (libname);
+ if (NULL != (dot = strstr (basename, ".")))
+ *dot = '\0';
+ lib_ret = GNUNET_PLUGIN_load (basename, lac->arg);
+ if (NULL != lib_ret)
+ lac->cb (lac->cb_cls, basename, lib_ret);
+ GNUNET_free (basename);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Load all compatible plugins with the given base name.
+ *
+ * Note that the library must export symbols called
+ * "basename_ANYTHING_init" and "basename_ANYTHING__done". These will
+ * be called when the library is loaded and unloaded respectively.
+ *
+ * @param basename basename of the plugins to load
+ * @param arg argument to the plugin initialization function
+ * @param cb function to call for each plugin found
+ * @param cb_cls closure for 'cb'
+ */
+void
+GNUNET_PLUGIN_load_all (const char *basename, void *arg,
+ GNUNET_PLUGIN_LoaderCallback cb, void *cb_cls)
+{
+ struct LoadAllContext lac;
+ char *path;
+
+ path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+ if (path == NULL)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not determine plugin installation path.\n"));
+ return;
+ }
+ lac.basename = basename;
+ lac.arg = arg;
+ lac.cb = cb;
+ lac.cb_cls = cb_cls;
+ GNUNET_DISK_directory_scan (path, &find_libraries, &lac);
+ GNUNET_free (path);
+}
+
+
+/* end of plugin.c */
diff --git a/src/util/program.c b/src/util/program.c
new file mode 100644
index 0000000..6a0e5a5
--- /dev/null
+++ b/src/util/program.c
@@ -0,0 +1,261 @@
+/*
+ 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_resolver_service.h"
+#include "gnunet_scheduler_lib.h"
+#include <gcrypt.h>
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * 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.
+ */
+ const 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;
+
+ GNUNET_RESOLVER_connect (cc->cfg);
+ cc->task (cc->task_cls, cc->args, cc->cfgfile, cc->cfg);
+}
+
+
+/**
+ * Compare function for 'qsort' to sort command-line arguments by the
+ * short option.
+ *
+ * @param a1 first command line option
+ * @param a2 second command line 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 ((unsigned char) c1->shortName) >
+ toupper ((unsigned char) c2->shortName))
+ return 1;
+ if (toupper ((unsigned char) c1->shortName) <
+ toupper ((unsigned char) 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 binaryHelp help text for the program
+ * @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;
+ char *logfile;
+ int ret;
+ unsigned int cnt;
+ unsigned long long skew_offset;
+ unsigned long long skew_variance;
+ long long clock_offset;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ 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_LOGFILE (&logfile),
+ GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION)
+ };
+ struct GNUNET_GETOPT_CommandLineOption *allopts;
+ const char *gargs;
+ char *lpfx;
+ char *spc;
+
+ logfile = NULL;
+ gargs = getenv ("GNUNET_ARGS");
+ if (gargs != NULL)
+ {
+ char **gargv;
+ unsigned int gargc;
+ int i;
+ char *tok;
+ char *cargs;
+
+ gargv = NULL;
+ gargc = 0;
+ for (i = 0; i < argc; i++)
+ GNUNET_array_append (gargv, gargc, GNUNET_strdup (argv[i]));
+ cargs = GNUNET_strdup (gargs);
+ tok = strtok (cargs, " ");
+ while (NULL != tok)
+ {
+ GNUNET_array_append (gargv, gargc, GNUNET_strdup (tok));
+ tok = strtok (NULL, " ");
+ }
+ GNUNET_free (cargs);
+ GNUNET_array_append (gargv, gargc, NULL);
+ argv = (char *const *) gargv;
+ argc = gargc - 1;
+ }
+ memset (&cc, 0, sizeof (cc));
+ loglev = NULL;
+ cc.task = task;
+ cc.task_cls = task_cls;
+ cc.cfg = 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 = NULL;
+ cc.cfgfile = GNUNET_strdup (GNUNET_DEFAULT_USER_CONFIG_FILE);
+ lpfx = GNUNET_strdup (binaryName);
+ if (NULL != (spc = strstr (lpfx, " ")))
+ *spc = '\0';
+ if ((-1 ==
+ (ret =
+ GNUNET_GETOPT_run (binaryName, allopts, (unsigned int) argc, argv))) ||
+ (GNUNET_OK != GNUNET_log_setup (lpfx, loglev, logfile)))
+ {
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free_non_null (cc.cfgfile);
+ GNUNET_free_non_null (loglev);
+ GNUNET_free_non_null (logfile);
+ GNUNET_free (allopts);
+ GNUNET_free (lpfx);
+ return GNUNET_SYSERR;
+ }
+ (void) GNUNET_CONFIGURATION_load (cfg, cc.cfgfile);
+ GNUNET_free (allopts);
+ GNUNET_free (lpfx);
+ if (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (cc.cfg, "testing", "skew_offset",
+ &skew_offset) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (cc.cfg, "testing",
+ "skew_variance", &skew_variance)))
+ {
+ clock_offset = skew_offset - skew_variance;
+ GNUNET_TIME_set_offset (clock_offset);
+ }
+ /* run */
+ cc.args = &argv[ret];
+ GNUNET_SCHEDULER_run (&program_main, &cc);
+
+ /* clean up */
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_free_non_null (cc.cfgfile);
+ GNUNET_free_non_null (loglev);
+ GNUNET_free_non_null (logfile);
+ return GNUNET_OK;
+}
+
+
+/* end of program.c */
diff --git a/src/util/pseudonym.c b/src/util/pseudonym.c
new file mode 100644
index 0000000..782a405
--- /dev/null
+++ b/src/util/pseudonym.c
@@ -0,0 +1,602 @@
+/*
+ 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"
+#include "gnunet_bio_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+/**
+ * Name of the directory which stores meta data for pseudonym
+ */
+#define PS_METADATA_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms" DIR_SEPARATOR_STR "metadata" DIR_SEPARATOR_STR
+
+/**
+ * Name of the directory which stores names for pseudonyms
+ */
+#define PS_NAMES_DIR DIR_SEPARATOR_STR "data" DIR_SEPARATOR_STR "pseudonyms" DIR_SEPARATOR_STR "names" DIR_SEPARATOR_STR
+
+
+/**
+ * Configuration section we use.
+ */
+#define GNUNET_CLIENT_SERVICE_NAME "client"
+
+
+
+/**
+ * Registered callbacks for discovery of pseudonyms.
+ */
+struct DiscoveryCallback
+{
+ /**
+ * This is a linked list.
+ */
+ struct DiscoveryCallback *next;
+
+ /**
+ * Function to call each time a pseudonym is discovered.
+ */
+ GNUNET_PSEUDONYM_Iterator callback;
+
+ /**
+ * Closure for callback.
+ */
+ void *closure;
+};
+
+
+/**
+ * Head of the linked list of functions to call when
+ * new pseudonyms are added.
+ */
+static struct DiscoveryCallback *head;
+
+/**
+ * Internal notification about new tracked URI.
+ * @param id a point to the hash code of pseudonym
+ * @param md meta data to be written
+ * @param rating rating of pseudonym
+ */
+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.
+ * @param cfg configuration to use
+ * @param iterator iterator over pseudonym
+ * @param closure point to a closure
+ */
+int
+GNUNET_PSEUDONYM_discovery_callback_register (const 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.
+ * @param iterator iterator over pseudonym
+ * @param closure point to a closure
+ */
+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.
+ * @param cfg configuration to use
+ * @param prefix path components to append to the private directory name
+ * @param psid hash code of pseudonym, can be NULL
+ * @return filename of the pseudonym (if psid != NULL) or directory with the data (if psid == NULL)
+ */
+static char *
+get_data_filename (const 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);
+}
+
+
+/**
+ * Write the pseudonym infomation into a file
+ * @param cfg configuration to use
+ * @param nsid hash code of a pseudonym
+ * @param meta meta data to be written into a file
+ * @param ranking ranking of a pseudonym
+ * @param ns_name name of a pseudonym
+ */
+static void
+write_pseudonym_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const GNUNET_HashCode * nsid,
+ const struct GNUNET_CONTAINER_MetaData *meta,
+ int32_t ranking, const char *ns_name)
+{
+ char *fn;
+ struct GNUNET_BIO_WriteHandle *fileW;
+
+ fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
+ GNUNET_assert (fn != NULL);
+ fileW = GNUNET_BIO_write_open (fn);
+ if (NULL != fileW)
+ {
+ if ((GNUNET_OK != GNUNET_BIO_write_int32 (fileW, ranking)) ||
+ (GNUNET_OK != GNUNET_BIO_write_string (fileW, ns_name)) ||
+ (GNUNET_OK != GNUNET_BIO_write_meta_data (fileW, meta)))
+ {
+ (void) GNUNET_BIO_write_close (fileW);
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
+ GNUNET_free (fn);
+ return;
+ }
+ if (GNUNET_OK != GNUNET_BIO_write_close (fileW))
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
+ GNUNET_free (fn);
+ return;
+ }
+ }
+ GNUNET_free (fn);
+ /* create entry for pseudonym name in names */
+ /* FIXME: 90% of what this call does is not needed
+ * here => refactor code to only create the entry! */
+ GNUNET_free_non_null (GNUNET_PSEUDONYM_id_to_name (cfg, nsid));
+}
+
+
+/**
+ * read the pseudonym infomation from a file
+ * @param cfg configuration to use
+ * @param nsid hash code of a pseudonym
+ * @param meta meta data to be read from a file
+ * @param ranking ranking of a pseudonym
+ * @param ns_name name of a pseudonym
+ */
+static int
+read_info (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const GNUNET_HashCode * nsid,
+ struct GNUNET_CONTAINER_MetaData **meta, int32_t * ranking,
+ char **ns_name)
+{
+ char *fn;
+ char *emsg;
+ struct GNUNET_BIO_ReadHandle *fileR;
+
+ fn = get_data_filename (cfg, PS_METADATA_DIR, nsid);
+ GNUNET_assert (fn != NULL);
+ fileR = GNUNET_BIO_read_open (fn);
+ if (fileR == NULL)
+ {
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ emsg = NULL;
+ *ns_name = NULL;
+ if ((GNUNET_OK != GNUNET_BIO_read_int32 (fileR, ranking)) ||
+ (GNUNET_OK !=
+ GNUNET_BIO_read_string (fileR, "Read string error!", ns_name, 200)) ||
+ (GNUNET_OK !=
+ GNUNET_BIO_read_meta_data (fileR, "Read meta data error!", meta)))
+ {
+ (void) GNUNET_BIO_read_close (fileR, &emsg);
+ GNUNET_free_non_null (emsg);
+ GNUNET_free_non_null (*ns_name);
+ *ns_name = NULL;
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ if (GNUNET_OK != GNUNET_BIO_read_close (fileR, &emsg))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Failed to parse metadata about pseudonym from file `%s': %s\n"), fn,
+ emsg);
+ GNUNET_break (GNUNET_OK == GNUNET_DISK_directory_remove (fn));
+ GNUNET_CONTAINER_meta_data_destroy (*meta);
+ *meta = NULL;
+ GNUNET_free_non_null (*ns_name);
+ *ns_name = NULL;
+ GNUNET_free_non_null (emsg);
+ GNUNET_free (fn);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free (fn);
+ return GNUNET_OK;
+}
+
+
+
+/**
+ * Return the unique, human readable name for the given namespace.
+ *
+ * @param cfg configuration
+ * @param nsid cryptographic ID of the namespace
+ * @return NULL on failure (should never happen)
+ */
+char *
+GNUNET_PSEUDONYM_id_to_name (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const GNUNET_HashCode * nsid)
+{
+ struct GNUNET_CONTAINER_MetaData *meta;
+ char *name;
+ GNUNET_HashCode nh;
+ char *fn;
+ uint64_t len;
+ struct GNUNET_DISK_FileHandle *fh;
+ unsigned int i;
+ unsigned int idx;
+ char *ret;
+ struct stat sbuf;
+ int32_t temp = 0;
+ int32_t *rank = &temp;
+
+ meta = NULL;
+ name = NULL;
+ if (GNUNET_OK == read_info (cfg, nsid, &meta, rank, &name))
+ {
+ if ((meta != NULL) && (name == NULL))
+ name =
+ GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+ EXTRACTOR_METATYPE_TITLE,
+ EXTRACTOR_METATYPE_GNUNET_ORIGINAL_FILENAME,
+ EXTRACTOR_METATYPE_FILENAME,
+ EXTRACTOR_METATYPE_DESCRIPTION,
+ EXTRACTOR_METATYPE_SUBJECT,
+ EXTRACTOR_METATYPE_PUBLISHER,
+ EXTRACTOR_METATYPE_AUTHOR_NAME,
+ EXTRACTOR_METATYPE_COMMENT,
+ EXTRACTOR_METATYPE_SUMMARY,
+ -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);
+ fh = GNUNET_DISK_file_open (fn,
+ GNUNET_DISK_OPEN_CREATE |
+ GNUNET_DISK_OPEN_READWRITE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ i = 0;
+ idx = -1;
+ while ((len >= sizeof (GNUNET_HashCode)) &&
+ (sizeof (GNUNET_HashCode) ==
+ GNUNET_DISK_file_read (fh, &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) !=
+ GNUNET_DISK_file_write (fh, nsid, sizeof (GNUNET_HashCode)))
+ LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "write", fn);
+ }
+ GNUNET_DISK_file_close (fh);
+ 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.
+ *
+ * @param cfg configuration to use
+ * @param ns_uname human-readable name for the namespace
+ * @param nsid set to namespace ID based on 'ns_uname'
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_PSEUDONYM_name_to_id (const struct GNUNET_CONFIGURATION_Handle *cfg,
+ const char *ns_uname, GNUNET_HashCode * nsid)
+{
+ size_t slen;
+ uint64_t len;
+ unsigned int idx;
+ char *name;
+ GNUNET_HashCode nh;
+ char *fn;
+ struct GNUNET_DISK_FileHandle *fh;
+
+ 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;
+ }
+ fh = GNUNET_DISK_file_open (fn,
+ GNUNET_DISK_OPEN_CREATE |
+ GNUNET_DISK_OPEN_READWRITE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ GNUNET_free (fn);
+ GNUNET_DISK_file_seek (fh, idx * sizeof (GNUNET_HashCode),
+ GNUNET_DISK_SEEK_SET);
+ if (sizeof (GNUNET_HashCode) !=
+ GNUNET_DISK_file_read (fh, nsid, sizeof (GNUNET_HashCode)))
+ {
+ GNUNET_DISK_file_close (fh);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_DISK_file_close (fh);
+ return GNUNET_OK;
+}
+
+
+
+/**
+ * struct used to list the pseudonym
+ */
+struct ListPseudonymClosure
+{
+
+ /**
+ * iterator over pseudonym
+ */
+ GNUNET_PSEUDONYM_Iterator iterator;
+
+ /**
+ * Closure for iterator.
+ */
+ void *closure;
+
+ /**
+ * Configuration to use.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+};
+
+
+
+/**
+ * the help function to list all available pseudonyms
+ * @param cls point to a struct ListPseudonymClosure
+ * @param fullname name of pseudonym
+ */
+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;
+ char *str;
+
+ 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 */
+ str = NULL;
+ if (GNUNET_OK != read_info (c->cfg, &id, &meta, &rating, &str))
+ return GNUNET_OK; /* ignore entry */
+ GNUNET_free_non_null (str);
+ if (c->iterator != NULL)
+ ret = c->iterator (c->closure, &id, meta, rating);
+ GNUNET_CONTAINER_meta_data_destroy (meta);
+ return ret;
+}
+
+
+/**
+ * List all available pseudonyms.
+ *
+ * @param cfg overall configuration
+ * @param iterator function to call for each pseudonym
+ * @param closure closure for iterator
+ * @return number of pseudonyms found
+ */
+int
+GNUNET_PSEUDONYM_list_all (const 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 cfg overall configuration
+ * @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 (const 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;
+}
+
+
+/**
+ * Add a pseudonym to the set of known pseudonyms.
+ * For all pseudonym advertisements that we discover
+ * FS should automatically call this function.
+ *
+ * @param cfg overall configuration
+ * @param id the pseudonym identifier
+ * @param meta metadata for the pseudonym
+ */
+void
+GNUNET_PSEUDONYM_add (const 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_merge (old, meta);
+ 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/resolver.conf.in b/src/util/resolver.conf.in
new file mode 100644
index 0000000..671ea0e
--- /dev/null
+++ b/src/util/resolver.conf.in
@@ -0,0 +1,22 @@
+[resolver]
+AUTOSTART = YES
+@UNIXONLY@ PORT = 2089
+HOSTNAME = localhost
+HOME = $SERVICEHOME
+CONFIG = $DEFAULTCONFIG
+BINARY = gnunet-service-resolver
+ACCEPT_FROM = 127.0.0.1;
+ACCEPT_FROM6 = ::1;
+UNIXPATH = /tmp/gnunet-service-resolver.sock
+UNIX_MATCH_UID = NO
+UNIX_MATCH_GID = NO
+# DISABLE_SOCKET_FORWARDING = NO
+# USERNAME =
+# MAXBUF =
+# TIMEOUT =
+# DISABLEV6 =
+# BINDTO =
+# REJECT_FROM =
+# REJECT_FROM6 =
+# PREFIX =
+
diff --git a/src/util/resolver.h b/src/util/resolver.h
new file mode 100644
index 0000000..2c0de99
--- /dev/null
+++ b/src/util/resolver.h
@@ -0,0 +1,69 @@
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2012 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/resolver.h
+ */
+#ifndef RESOLVER_H
+#define RESOLVER_H
+
+#include "gnunet_common.h"
+
+#define DEBUG_RESOLVER GNUNET_EXTRA_LOGGING
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * 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 (0-terminated hostname or struct
+ * in[6]_addr, 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_RESOLVER_REQUEST
+ */
+ struct GNUNET_MessageHeader header;
+
+ /**
+ * GNUNET_YES to get hostname from IP,
+ * GNUNET_NO to get IP from hostname.
+ */
+ int32_t direction GNUNET_PACKED;
+
+ /**
+ * Address family to use (AF_INET, AF_INET6 or AF_UNSPEC).
+ */
+ int32_t af GNUNET_PACKED;
+
+ /* followed by 0-terminated string for A/AAAA-lookup or
+ by 'struct in_addr' / 'struct in6_addr' for reverse lookup */
+
+};
+GNUNET_NETWORK_STRUCT_END
+
+#endif
diff --git a/src/util/resolver_api.c b/src/util/resolver_api.c
new file mode 100644
index 0000000..9d5ae6c
--- /dev/null
+++ b/src/util/resolver_api.c
@@ -0,0 +1,972 @@
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2011 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/resolver_api.c
+ * @brief resolver for writing a tool
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_getopt_lib.h"
+#include "gnunet_os_lib.h"
+#include "gnunet_client_lib.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_protocols.h"
+#include "gnunet_resolver_service.h"
+#include "gnunet_server_lib.h"
+#include "resolver.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "resolver-api", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "resolver-api", syscall)
+
+/**
+ * Maximum supported length for a hostname
+ */
+#define MAX_HOSTNAME 1024
+
+
+/**
+ * Possible hostnames for "loopback".
+ */
+static const char *loopback[] = {
+ "localhost",
+ "ip6-localnet",
+ NULL
+};
+
+
+/**
+ * Configuration.
+ */
+static const struct GNUNET_CONFIGURATION_Handle *resolver_cfg;
+
+/**
+ * Our connection to the resolver service, created on-demand, but then
+ * persists until error or shutdown.
+ */
+static struct GNUNET_CLIENT_Connection *client;
+
+/**
+ * Head of DLL of requests.
+ */
+static struct GNUNET_RESOLVER_RequestHandle *req_head;
+
+/**
+ * Tail of DLL of requests.
+ */
+static struct GNUNET_RESOLVER_RequestHandle *req_tail;
+
+/**
+ * How long should we wait to reconnect?
+ */
+static struct GNUNET_TIME_Relative backoff;
+
+/**
+ * Task for reconnecting.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier r_task;
+
+/**
+ * Task ID of shutdown task; only present while we have a
+ * connection to the resolver service.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier s_task;
+
+
+/**
+ * Handle to a request given to the resolver. Can be used to cancel
+ * the request prior to the timeout or successful execution. Also
+ * used to track our internal state for the request.
+ */
+struct GNUNET_RESOLVER_RequestHandle
+{
+
+ /**
+ * Next entry in DLL of requests.
+ */
+ struct GNUNET_RESOLVER_RequestHandle *next;
+
+ /**
+ * Previous entry in DLL of requests.
+ */
+ struct GNUNET_RESOLVER_RequestHandle *prev;
+
+ /**
+ * Callback if this is an name resolution request,
+ * otherwise NULL.
+ */
+ GNUNET_RESOLVER_AddressCallback addr_callback;
+
+ /**
+ * Callback if this is a reverse lookup request,
+ * otherwise NULL.
+ */
+ GNUNET_RESOLVER_HostnameCallback name_callback;
+
+ /**
+ * Closure for the respective "callback".
+ */
+ void *cls;
+
+ /**
+ * When should this request time out?
+ */
+ struct GNUNET_TIME_Absolute timeout;
+
+ /**
+ * Task handle for numeric lookups.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier task;
+
+ /**
+ * Desired address family.
+ */
+ int af;
+
+ /**
+ * Has this request been transmitted to the service?
+ * GNUNET_YES if transmitted
+ * GNUNET_YES if not transmitted
+ * GNUNET_SYSERR when request was canceled
+ */
+ int was_transmitted;
+
+ /**
+ * Did we add this request to the queue?
+ */
+ int was_queued;
+
+ /**
+ * Desired direction (IP to name or name to IP)
+ */
+ int direction;
+
+ /**
+ * GNUNET_YES if a response was received
+ */
+ int received_response;
+
+ /**
+ * Length of the data that follows this struct.
+ */
+ size_t data_len;
+};
+
+
+/**
+ * Check that the resolver service runs on localhost
+ * (or equivalent).
+ */
+static void
+check_config ()
+{
+ char *hostname;
+ unsigned int i;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+
+ memset (&v4, 0, sizeof (v4));
+ v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ memset (&v6, 0, sizeof (v6));
+ v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = sizeof (v6);
+#endif
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (resolver_cfg, "resolver",
+ "HOSTNAME", &hostname))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Must specify `%s' for `%s' in configuration!\n"), "HOSTNAME",
+ "resolver");
+ GNUNET_assert (0);
+ }
+ if ((1 != inet_pton (AF_INET, hostname, &v4)) ||
+ (1 != inet_pton (AF_INET6, hostname, &v6)))
+ {
+ GNUNET_free (hostname);
+ return;
+ }
+ i = 0;
+ while (loopback[i] != NULL)
+ if (0 == strcasecmp (loopback[i++], hostname))
+ {
+ GNUNET_free (hostname);
+ return;
+ }
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Must specify `%s' or numeric IP address for `%s' of `%s' in configuration!\n"),
+ "localhost", "HOSTNAME", "resolver");
+ GNUNET_free (hostname);
+ GNUNET_assert (0);
+}
+
+
+/**
+ * Create the connection to the resolver service.
+ *
+ * @param cfg configuration to use
+ */
+void
+GNUNET_RESOLVER_connect (const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ GNUNET_assert (NULL != cfg);
+ backoff = GNUNET_TIME_UNIT_MILLISECONDS;
+ resolver_cfg = cfg;
+ check_config ();
+}
+
+
+/**
+ * Destroy the connection to the resolver service.
+ */
+void
+GNUNET_RESOLVER_disconnect ()
+{
+ GNUNET_assert (NULL == req_head);
+ GNUNET_assert (NULL == req_tail);
+ if (NULL != client)
+ {
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from DNS service\n");
+#endif
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ client = NULL;
+ }
+ if (r_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (r_task);
+ r_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (s_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (s_task);
+ s_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+}
+
+
+/**
+ * Convert IP address to string without DNS resolution.
+ *
+ * @param af address family
+ * @param ip the address
+ * @param ip_len number of bytes in ip
+ * @return address as a string, NULL on error
+ */
+static char *
+no_resolve (int af,
+ const void *ip, socklen_t ip_len)
+{
+ char buf[INET6_ADDRSTRLEN];
+
+ switch (af)
+ {
+ case AF_INET:
+ if (ip_len != sizeof (struct in_addr))
+ return NULL;
+ if (NULL ==
+ inet_ntop (AF_INET, ip, buf, sizeof (buf)))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
+ return NULL;
+ }
+ break;
+ case AF_INET6:
+ if (ip_len != sizeof (struct in6_addr))
+ return NULL;
+ if (NULL ==
+ inet_ntop (AF_INET6, ip, buf, sizeof (buf)))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "inet_ntop");
+ return NULL;
+ }
+ break;
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+ return GNUNET_strdup (buf);
+}
+
+
+/**
+ * Adjust exponential back-off and reconnect to the service.
+ */
+static void
+reconnect ();
+
+
+/**
+ * Process pending requests to the resolver.
+ */
+static void
+process_requests ();
+
+
+/**
+ * Process response with a hostname for a DNS lookup.
+ *
+ * @param cls our "struct GNUNET_RESOLVER_RequestHandle" context
+ * @param msg message with the hostname, NULL on error
+ */
+static void
+handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh = cls;
+ uint16_t size;
+
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Receiving response from DNS service\n");
+#endif
+ if (msg == NULL)
+ {
+ char buf[INET6_ADDRSTRLEN];
+
+ if (NULL != rh->name_callback)
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Timeout trying to resolve IP address `%s'.\n"),
+ inet_ntop (rh->af, (const void *) &rh[1], buf, sizeof(buf)));
+ else
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Timeout trying to resolve hostname `%s'.\n"),
+ (const char *) &rh[1]);
+ /* check if request was canceled */
+ if (rh->was_transmitted != GNUNET_SYSERR)
+ {
+ if (NULL != rh->name_callback)
+ {
+ /* no reverse lookup was successful, return ip as string */
+ if (rh->received_response == GNUNET_NO)
+ rh->name_callback (rh->cls,
+ no_resolve (rh->af,
+ &rh[1],
+ rh->data_len));
+ /* at least one reverse lookup was successful */
+ else
+ rh->name_callback (rh->cls, NULL);
+ }
+ if (NULL != rh->addr_callback)
+ rh->addr_callback (rh->cls, NULL, 0);
+ }
+ GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+ GNUNET_free (rh);
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ client = NULL;
+ reconnect ();
+ return;
+ }
+ if (GNUNET_MESSAGE_TYPE_RESOLVER_RESPONSE != ntohs (msg->type))
+ {
+ GNUNET_break (0);
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ client = NULL;
+ reconnect ();
+ return;
+ }
+ size = ntohs (msg->size);
+ /* message contains not data, just header */
+ if (size == sizeof (struct GNUNET_MessageHeader))
+ {
+ /* check if request was canceled */
+ if (rh->was_transmitted != GNUNET_SYSERR)
+ {
+ if (NULL != rh->name_callback)
+ rh->name_callback (rh->cls, NULL);
+ if (NULL != rh->addr_callback)
+ rh->addr_callback (rh->cls, NULL, 0);
+ }
+ GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+ GNUNET_free (rh);
+ process_requests ();
+ return;
+ }
+ /* return reverse lookup results to caller */
+ if (NULL != rh->name_callback)
+ {
+ const char *hostname;
+
+ hostname = (const char *) &msg[1];
+ if (hostname[size - sizeof (struct GNUNET_MessageHeader) - 1] != '\0')
+ {
+ GNUNET_break (0);
+ if (rh->was_transmitted != GNUNET_SYSERR)
+ rh->name_callback (rh->cls, NULL);
+ GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+ GNUNET_free (rh);
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ client = NULL;
+ reconnect ();
+ return;
+ }
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s' for IP `%s'.\n"),
+ hostname, GNUNET_a2s ((const void *) &rh[1], rh->data_len));
+#endif
+ if (rh->was_transmitted != GNUNET_SYSERR)
+ rh->name_callback (rh->cls, hostname);
+ rh->received_response = GNUNET_YES;
+ GNUNET_CLIENT_receive (client, &handle_response, rh,
+ GNUNET_TIME_absolute_get_remaining (rh->timeout));
+ }
+ /* return lookup results to caller */
+ if (NULL != rh->addr_callback)
+ {
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ const struct sockaddr *sa;
+ socklen_t salen;
+ const void *ip;
+ size_t ip_len;
+
+ ip = &msg[1];
+ ip_len = size - sizeof (struct GNUNET_MessageHeader);
+ if (ip_len == sizeof (struct in_addr))
+ {
+ memset (&v4, 0, sizeof (v4));
+ v4.sin_family = AF_INET;
+ v4.sin_addr = *(struct in_addr*) ip;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ salen = sizeof (v4);
+ sa = (const struct sockaddr *) &v4;
+ }
+ else if (ip_len == sizeof (struct in6_addr))
+ {
+ memset (&v6, 0, sizeof (v6));
+ v6.sin6_family = AF_INET6;
+ v6.sin6_addr = *(struct in6_addr*) ip;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = sizeof (v6);
+#endif
+ salen = sizeof (v6);
+ sa = (const struct sockaddr *) &v6;
+ }
+ else
+ {
+ GNUNET_break (0);
+ if (rh->was_transmitted != GNUNET_SYSERR)
+ rh->addr_callback (rh->cls, NULL, 0);
+ GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+ GNUNET_free (rh);
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ client = NULL;
+ reconnect ();
+ return;
+ }
+ rh->addr_callback (rh->cls, sa, salen);
+ GNUNET_CLIENT_receive (client, &handle_response, rh,
+ GNUNET_TIME_absolute_get_remaining (rh->timeout));
+ }
+}
+
+
+/**
+ * We've been asked to lookup the address for a hostname and were
+ * given a valid numeric string. Perform the callbacks for the
+ * numeric addresses.
+ *
+ * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
+ * @param tc unused scheduler context
+ */
+static void
+numeric_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh = cls;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+ const char *hostname;
+
+ memset (&v4, 0, sizeof (v4));
+ v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ memset (&v6, 0, sizeof (v6));
+ v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = sizeof (v6);
+#endif
+ hostname = (const char *) &rh[1];
+ if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET)) &&
+ (1 == inet_pton (AF_INET, hostname, &v4.sin_addr)))
+ {
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
+ if ((rh->af == AF_UNSPEC) &&
+ (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
+ {
+ /* this can happen on some systems IF "hostname" is "localhost" */
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+ }
+ rh->addr_callback (rh->cls, NULL, 0);
+ GNUNET_free (rh);
+ return;
+ }
+ if (((rh->af == AF_UNSPEC) || (rh->af == AF_INET6)) &&
+ (1 == inet_pton (AF_INET6, hostname, &v6.sin6_addr)))
+ {
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+ rh->addr_callback (rh->cls, NULL, 0);
+ GNUNET_free (rh);
+ return;
+ }
+ /* why are we here? this task should not have been scheduled! */
+ GNUNET_assert (0);
+ GNUNET_free (rh);
+}
+
+
+/**
+ * We've been asked to lookup the address for a hostname and were
+ * given a variant of "loopback". Perform the callbacks for the
+ * respective loopback numeric addresses.
+ *
+ * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
+ * @param tc unused scheduler context
+ */
+static void
+loopback_resolution (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh = cls;
+ struct sockaddr_in v4;
+ struct sockaddr_in6 v6;
+
+ memset (&v4, 0, sizeof (v4));
+ v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ v4.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ memset (&v6, 0, sizeof (v6));
+ v6.sin6_family = AF_INET6;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v6.sin6_len = sizeof (v6);
+#endif
+ v6.sin6_addr = in6addr_loopback;
+ switch (rh->af)
+ {
+ case AF_INET:
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
+ break;
+ case AF_INET6:
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+ break;
+ case AF_UNSPEC:
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v6, sizeof (v6));
+ rh->addr_callback (rh->cls, (const struct sockaddr *) &v4, sizeof (v4));
+ break;
+ default:
+ GNUNET_break (0);
+ break;
+ }
+ rh->addr_callback (rh->cls, NULL, 0);
+ GNUNET_free (rh);
+}
+
+
+/**
+ * Task executed on system shutdown.
+ */
+static void
+shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ s_task = GNUNET_SCHEDULER_NO_TASK;
+ GNUNET_RESOLVER_disconnect ();
+}
+
+
+/**
+ * Process pending requests to the resolver.
+ */
+static void
+process_requests ()
+{
+ struct GNUNET_RESOLVER_GetMessage *msg;
+ char buf[GNUNET_SERVER_MAX_MESSAGE_SIZE - 1];
+ struct GNUNET_RESOLVER_RequestHandle *rh;
+
+ if (NULL == client)
+ {
+ reconnect ();
+ return;
+ }
+ rh = req_head;
+ if (NULL == rh)
+ {
+ /* nothing to do, release socket really soon if there is nothing
+ * else happening... */
+ s_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MILLISECONDS,
+ &shutdown_task, NULL);
+ return;
+ }
+ if (GNUNET_YES == rh->was_transmitted)
+ return; /* waiting for reply */
+ msg = (struct GNUNET_RESOLVER_GetMessage *) buf;
+ msg->header.size =
+ htons (sizeof (struct GNUNET_RESOLVER_GetMessage) + rh->data_len);
+ msg->header.type = htons (GNUNET_MESSAGE_TYPE_RESOLVER_REQUEST);
+ msg->direction = htonl (rh->direction);
+ msg->af = htonl (rh->af);
+ memcpy (&msg[1], &rh[1], rh->data_len);
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Transmitting DNS resolution request to DNS service\n");
+#endif
+ if (GNUNET_OK !=
+ GNUNET_CLIENT_transmit_and_get_response (client, &msg->header,
+ GNUNET_TIME_absolute_get_remaining
+ (rh->timeout), GNUNET_YES,
+ &handle_response, rh))
+ {
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ client = NULL;
+ reconnect ();
+ return;
+ }
+ rh->was_transmitted = GNUNET_YES;
+}
+
+
+/**
+ * Now try to reconnect to the resolver service.
+ *
+ * @param cls NULL
+ * @param tc scheduler context
+ */
+static void
+reconnect_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ r_task = GNUNET_SCHEDULER_NO_TASK;
+ if (NULL == req_head)
+ return; /* no work pending */
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ return;
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Trying to connect to DNS service\n");
+#endif
+ client = GNUNET_CLIENT_connect ("resolver", resolver_cfg);
+ if (NULL == client)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Failed to connect, will try again later\n");
+ reconnect ();
+ return;
+ }
+ process_requests ();
+}
+
+
+/**
+ * Adjust exponential back-off and reconnect to the service.
+ */
+static void
+reconnect ()
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh;
+
+ if (GNUNET_SCHEDULER_NO_TASK != r_task)
+ return;
+ GNUNET_assert (NULL == client);
+ if (NULL != (rh = req_head))
+ {
+ switch (rh->was_transmitted)
+ {
+ case GNUNET_NO:
+ /* nothing more to do */
+ break;
+ case GNUNET_YES:
+ /* disconnected, transmit again! */
+ rh->was_transmitted = GNUNET_NO;
+ break;
+ case GNUNET_SYSERR:
+ /* request was cancelled, remove entirely */
+ GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+ GNUNET_free (rh);
+ break;
+ default:
+ GNUNET_assert (0);
+ break;
+ }
+ }
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Will try to connect to DNS service in %llu ms\n",
+ (unsigned long long) backoff.rel_value);
+#endif
+ GNUNET_assert (NULL != resolver_cfg);
+ r_task = GNUNET_SCHEDULER_add_delayed (backoff, &reconnect_task, NULL);
+ backoff = GNUNET_TIME_relative_multiply (backoff, 2);
+}
+
+
+/**
+ * Convert a string to one or more IP addresses.
+ *
+ * @param hostname the hostname to resolve
+ * @param af AF_INET or AF_INET6; use AF_UNSPEC for "any"
+ * @param callback function to call with addresses
+ * @param callback_cls closure for callback
+ * @param timeout how long to try resolving
+ * @return handle that can be used to cancel the request, NULL on error
+ */
+struct GNUNET_RESOLVER_RequestHandle *
+GNUNET_RESOLVER_ip_get (const char *hostname, int af,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_RESOLVER_AddressCallback callback,
+ void *callback_cls)
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh;
+ size_t slen;
+ unsigned int i;
+ struct in_addr v4;
+ struct in6_addr v6;
+
+ slen = strlen (hostname) + 1;
+ if (slen + sizeof (struct GNUNET_RESOLVER_GetMessage) >=
+ GNUNET_SERVER_MAX_MESSAGE_SIZE)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + slen);
+ rh->af = af;
+ rh->addr_callback = callback;
+ rh->cls = callback_cls;
+ memcpy (&rh[1], hostname, slen);
+ rh->data_len = slen;
+ rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ rh->direction = GNUNET_NO;
+ /* first, check if this is a numeric address */
+ if (((1 == inet_pton (AF_INET, hostname, &v4)) &&
+ ((af == AF_INET) || (af == AF_UNSPEC))) ||
+ ((1 == inet_pton (AF_INET6, hostname, &v6)) &&
+ ((af == AF_INET6) || (af == AF_UNSPEC))))
+ {
+ rh->task = GNUNET_SCHEDULER_add_now (&numeric_resolution, rh);
+ return rh;
+ }
+ /* then, check if this is a loopback address */
+ i = 0;
+ while (loopback[i] != NULL)
+ if (0 == strcasecmp (loopback[i++], hostname))
+ {
+ rh->task = GNUNET_SCHEDULER_add_now (&loopback_resolution, rh);
+ return rh;
+ }
+ GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
+ rh->was_queued = GNUNET_YES;
+ if (s_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (s_task);
+ s_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ process_requests ();
+ return rh;
+}
+
+
+/**
+ * We've been asked to convert an address to a string without
+ * a reverse lookup. Do it.
+ *
+ * @param cls struct GNUNET_RESOLVER_RequestHandle for the request
+ * @param tc unused scheduler context
+ */
+static void
+numeric_reverse (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh = cls;
+ char *result;
+
+ result = no_resolve (rh->af, &rh[1], rh->data_len);
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolver returns `%s'.\n"), result);
+#endif
+ if (result != NULL)
+ {
+ rh->name_callback (rh->cls, result);
+ GNUNET_free (result);
+ }
+ rh->name_callback (rh->cls, NULL);
+ GNUNET_free (rh);
+}
+
+
+/**
+ * Get an IP address as a string.
+ *
+ * @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
+ * last callback is NULL when finished
+ * @param cls closure for callback
+ * @return handle that can be used to cancel the request
+ */
+struct GNUNET_RESOLVER_RequestHandle *
+GNUNET_RESOLVER_hostname_get (const struct sockaddr *sa, socklen_t salen,
+ int do_resolve,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_RESOLVER_HostnameCallback callback,
+ void *cls)
+{
+ struct GNUNET_RESOLVER_RequestHandle *rh;
+ size_t ip_len;
+ const void *ip;
+
+ check_config ();
+ switch (sa->sa_family)
+ {
+ case AF_INET:
+ ip_len = sizeof (struct in_addr);
+ ip = &((const struct sockaddr_in*)sa)->sin_addr;
+ break;
+ case AF_INET6:
+ ip_len = sizeof (struct in6_addr);
+ ip = &((const struct sockaddr_in6*)sa)->sin6_addr;
+ break;
+ default:
+ GNUNET_break (0);
+ return NULL;
+ }
+ rh = GNUNET_malloc (sizeof (struct GNUNET_RESOLVER_RequestHandle) + salen);
+ rh->name_callback = callback;
+ rh->cls = cls;
+ rh->af = sa->sa_family;
+ rh->timeout = GNUNET_TIME_relative_to_absolute (timeout);
+ memcpy (&rh[1], ip, ip_len);
+ rh->data_len = ip_len;
+ rh->direction = GNUNET_YES;
+ rh->received_response = GNUNET_NO;
+ if (GNUNET_NO == do_resolve)
+ {
+ rh->task = GNUNET_SCHEDULER_add_now (&numeric_reverse, rh);
+ return rh;
+ }
+ GNUNET_CONTAINER_DLL_insert_tail (req_head, req_tail, rh);
+ rh->was_queued = GNUNET_YES;
+ if (s_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (s_task);
+ s_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ process_requests ();
+ return rh;
+}
+
+
+/**
+ * Get local fully qualified af name
+ *
+ * @return fqdn
+ */
+char *
+GNUNET_RESOLVER_local_fqdn_get ()
+{
+ struct hostent *host;
+ char hostname[GNUNET_OS_get_hostname_max_length () + 1];
+
+ if (0 != gethostname (hostname, sizeof (hostname) - 1))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "gethostname");
+ return NULL;
+ }
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our FQDN `%s'\n"), hostname);
+#endif
+ host = gethostbyname (hostname);
+ if (NULL == host)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Could not resolve our FQDN : %s\n"),
+ hstrerror (h_errno));
+ return NULL;
+ }
+ return GNUNET_strdup (host->h_name);
+}
+
+
+/**
+ * Looking our own hostname.
+ *
+ * @param af 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
+ * @return handle that can be used to cancel the request, NULL on error
+ */
+struct GNUNET_RESOLVER_RequestHandle *
+GNUNET_RESOLVER_hostname_resolve (int af,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_RESOLVER_AddressCallback callback,
+ void *cls)
+{
+ char hostname[GNUNET_OS_get_hostname_max_length () + 1];
+
+ if (0 != gethostname (hostname, sizeof (hostname) - 1))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "gethostname");
+ return NULL;
+ }
+#if DEBUG_RESOLVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our hostname `%s'\n"), hostname);
+#endif
+ return GNUNET_RESOLVER_ip_get (hostname, af, timeout, callback, cls);
+}
+
+
+/**
+ * Cancel a request that is still pending with the resolver.
+ * Note that a client MUST NOT cancel a request that has
+ * been completed (i.e, the callback has been called to
+ * signal timeout or the final result).
+ *
+ * @param rh handle of request to cancel
+ */
+void
+GNUNET_RESOLVER_request_cancel (struct GNUNET_RESOLVER_RequestHandle *rh)
+{
+ if (rh->task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (rh->task);
+ rh->task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (rh->was_transmitted == GNUNET_NO)
+ {
+ if (rh->was_queued == GNUNET_YES)
+ GNUNET_CONTAINER_DLL_remove (req_head, req_tail, rh);
+ GNUNET_free (rh);
+ return;
+ }
+ GNUNET_assert (rh->was_transmitted == GNUNET_YES);
+ rh->was_transmitted = GNUNET_SYSERR; /* mark as cancelled */
+}
+
+
+/* end of resolver_api.c */
diff --git a/src/util/scheduler.c b/src/util/scheduler.c
new file mode 100644
index 0000000..c54672f
--- /dev/null
+++ b/src/util/scheduler.c
@@ -0,0 +1,1696 @@
+/*
+ This file is part of GNUnet
+ (C) 2009, 2011 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.c
+ * @brief schedule computations using continuation passing style
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_os_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_signal_lib.h"
+#include "gnunet_time_lib.h"
+#include "disk.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util-scheduler", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util-scheduler", syscall)
+
+
+#ifdef LINUX
+#include "execinfo.h"
+
+/**
+ * Use lsof to generate file descriptor reports on select error?
+ * (turn off for stable releases).
+ */
+#define USE_LSOF GNUNET_NO
+
+/**
+ * Obtain trace information for all scheduler calls that schedule tasks.
+ */
+#define EXECINFO GNUNET_NO
+
+/**
+ * Check each file descriptor before adding
+ */
+#define DEBUG_FDS GNUNET_NO
+
+/**
+ * Depth of the traces collected via EXECINFO.
+ */
+#define MAX_TRACE_DEPTH 50
+#endif
+
+/**
+ * Should we figure out which tasks are delayed for a while
+ * before they are run? (Consider using in combination with EXECINFO).
+ */
+#define PROFILE_DELAYS GNUNET_NO
+
+/**
+ * Task that were in the queue for longer than this are reported if
+ * PROFILE_DELAYS is active.
+ */
+#define DELAY_THRESHOLD GNUNET_TIME_UNIT_SECONDS
+
+
+/**
+ * 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.
+ */
+ struct GNUNET_NETWORK_FDSet *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.
+ */
+ struct GNUNET_NETWORK_FDSet *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;
+
+#if PROFILE_DELAYS
+ /**
+ * When was the task scheduled?
+ */
+ struct GNUNET_TIME_Absolute start_time;
+#endif
+
+ /**
+ * 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;
+
+ /**
+ * Set if we only wait for reading from a single FD, otherwise -1.
+ */
+ int read_fd;
+
+ /**
+ * Set if we only wait for writing to a single FD, otherwise -1.
+ */
+ int write_fd;
+
+ /**
+ * Should the existence of this task in the queue be counted as
+ * reason to not shutdown the scheduler?
+ */
+ int lifeness;
+
+#if EXECINFO
+ /**
+ * Array of strings which make up a backtrace from the point when this
+ * task was scheduled (essentially, who scheduled the task?)
+ */
+ char **backtrace_strings;
+
+ /**
+ * Size of the backtrace_strings array
+ */
+ int num_backtrace_strings;
+#endif
+
+
+};
+
+
+/**
+ * List of tasks waiting for an event.
+ */
+static struct Task *pending;
+
+/**
+ * List of tasks waiting ONLY for a timeout event.
+ * Sorted by timeout (earliest first). Used so that
+ * we do not traverse the list of these tasks when
+ * building select sets (we just look at the head
+ * to determine the respective timeout ONCE).
+ */
+static struct Task *pending_timeout;
+
+/**
+ * Last inserted task waiting ONLY for a timeout event.
+ * Used to (heuristically) speed up insertion.
+ */
+static struct Task *pending_timeout_last;
+
+/**
+ * ID of the task that is running right now.
+ */
+static struct Task *active_task;
+
+/**
+ * List of tasks ready to run right now,
+ * grouped by importance.
+ */
+static 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).
+ */
+static 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.
+ */
+static GNUNET_SCHEDULER_TaskIdentifier lowest_pending_id;
+
+/**
+ * Number of tasks on the ready list.
+ */
+static unsigned int ready_count;
+
+/**
+ * How many tasks have we run so far?
+ */
+static unsigned long long tasks_run;
+
+/**
+ * Priority of the task running right now. Only
+ * valid while a task is running.
+ */
+static enum GNUNET_SCHEDULER_Priority current_priority;
+
+/**
+ * Priority of the highest task added in the current select
+ * iteration.
+ */
+static enum GNUNET_SCHEDULER_Priority max_priority_added;
+
+/**
+ * Value of the 'lifeness' flag for the current task.
+ */
+static int current_lifeness;
+
+/**
+ * Function to use as a select() in the scheduler.
+ * If NULL, we use GNUNET_NETWORK_socket_select ().
+ */
+static GNUNET_SCHEDULER_select scheduler_select;
+
+/**
+ * Closure for 'scheduler_select'.
+ */
+static void *scheduler_select_cls;
+
+/**
+ * Sets the select function to use in the scheduler (scheduler_select).
+ *
+ * @param new_select new select function to use
+ * @param new_select_cls closure for 'new_select'
+ * @return previously used select function, NULL for default
+ */
+void
+GNUNET_SCHEDULER_set_select (GNUNET_SCHEDULER_select new_select,
+ void *new_select_cls)
+{
+ scheduler_select = new_select;
+ scheduler_select_cls = new_select_cls;
+}
+
+
+/**
+ * Check that the given priority is legal (and return it).
+ *
+ * @param p priority value to check
+ * @return p on success, 0 on error
+ */
+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 */
+}
+
+
+/**
+ * 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).
+ *
+ * @param id which task are we checking for
+ * @return GNUNET_YES if so, GNUNET_NO if not
+ */
+static int
+is_pending (GNUNET_SCHEDULER_TaskIdentifier id)
+{
+ struct Task *pos;
+ enum GNUNET_SCHEDULER_Priority p;
+ GNUNET_SCHEDULER_TaskIdentifier min;
+
+ if (id < lowest_pending_id)
+ return GNUNET_NO;
+ min = -1; /* maximum value */
+ pos = pending;
+ while (pos != NULL)
+ {
+ if (pos->id == id)
+ return GNUNET_YES;
+ if (pos->id < min)
+ min = pos->id;
+ pos = pos->next;
+ }
+ pos = pending_timeout;
+ 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 = ready[p];
+ while (pos != NULL)
+ {
+ if (pos->id == id)
+ return GNUNET_YES;
+ if (pos->id < min)
+ min = pos->id;
+ pos = pos->next;
+ }
+ }
+ lowest_pending_id = min;
+ return GNUNET_NO;
+}
+
+
+/**
+ * Update all sets and timeout for select.
+ *
+ * @param rs read-set, set to all FDs we would like to read (updated)
+ * @param ws write-set, set to all FDs we would like to write (updated)
+ * @param timeout next timeout (updated)
+ */
+static void
+update_sets (struct GNUNET_NETWORK_FDSet *rs, struct GNUNET_NETWORK_FDSet *ws,
+ struct GNUNET_TIME_Relative *timeout)
+{
+ struct Task *pos;
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Relative to;
+
+ now = GNUNET_TIME_absolute_get ();
+ pos = pending_timeout;
+ if (pos != NULL)
+ {
+ to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
+ if (timeout->rel_value > to.rel_value)
+ *timeout = to;
+ if (pos->reason != 0)
+ *timeout = GNUNET_TIME_UNIT_ZERO;
+ }
+ pos = pending;
+ while (pos != NULL)
+ {
+ if ((pos->prereq_id != GNUNET_SCHEDULER_NO_TASK) &&
+ (GNUNET_YES == is_pending (pos->prereq_id)))
+ {
+ pos = pos->next;
+ continue;
+ }
+ if (pos->timeout.abs_value != GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
+ {
+ to = GNUNET_TIME_absolute_get_difference (now, pos->timeout);
+ if (timeout->rel_value > to.rel_value)
+ *timeout = to;
+ }
+ if (pos->read_fd != -1)
+ GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
+ if (pos->write_fd != -1)
+ GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
+ if (pos->read_set != NULL)
+ GNUNET_NETWORK_fdset_add (rs, pos->read_set);
+ if (pos->write_set != NULL)
+ GNUNET_NETWORK_fdset_add (ws, pos->write_set);
+ if (pos->reason != 0)
+ *timeout = GNUNET_TIME_UNIT_ZERO;
+ 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 ready set that is ready
+ * @param want set that we want to be ready
+ * @return GNUNET_YES if there was some overlap
+ */
+static int
+set_overlaps (const struct GNUNET_NETWORK_FDSet *ready,
+ struct GNUNET_NETWORK_FDSet *want)
+{
+ if ((NULL == want) || (NULL == ready))
+ return GNUNET_NO;
+ if (GNUNET_NETWORK_fdset_overlap (ready, want))
+ {
+ /* copy all over (yes, there maybe unrelated bits,
+ * but this should not hurt well-written clients) */
+ GNUNET_NETWORK_fdset_copy (want, ready);
+ return GNUNET_YES;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Check if the given task is eligible to run now.
+ * Also set the reason why it is eligible.
+ *
+ * @param task task to check if it is ready
+ * @param now the current time
+ * @param rs set of FDs ready for reading
+ * @param ws set of FDs ready for writing
+ * @return GNUNET_YES if we can run it, GNUNET_NO if not.
+ */
+static int
+is_ready (struct Task *task, struct GNUNET_TIME_Absolute now,
+ const struct GNUNET_NETWORK_FDSet *rs,
+ const struct GNUNET_NETWORK_FDSet *ws)
+{
+ enum GNUNET_SCHEDULER_Reason reason;
+
+ reason = task->reason;
+ if (now.abs_value >= task->timeout.abs_value)
+ reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
+ if ((0 == (reason & GNUNET_SCHEDULER_REASON_READ_READY)) &&
+ (((task->read_fd != -1) &&
+ (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (rs, task->read_fd))) ||
+ (set_overlaps (rs, task->read_set))))
+ reason |= GNUNET_SCHEDULER_REASON_READ_READY;
+ if ((0 == (reason & GNUNET_SCHEDULER_REASON_WRITE_READY)) &&
+ (((task->write_fd != -1) &&
+ (GNUNET_YES == GNUNET_NETWORK_fdset_test_native (ws, task->write_fd)))
+ || (set_overlaps (ws, task->write_set))))
+ reason |= GNUNET_SCHEDULER_REASON_WRITE_READY;
+ if (reason == 0)
+ return GNUNET_NO; /* not ready */
+ if (task->prereq_id != GNUNET_SCHEDULER_NO_TASK)
+ {
+ if (GNUNET_YES == is_pending (task->prereq_id))
+ {
+ task->reason = reason;
+ return GNUNET_NO; /* prereq waiting */
+ }
+ reason |= GNUNET_SCHEDULER_REASON_PREREQ_DONE;
+ }
+ task->reason = reason;
+ return GNUNET_YES;
+}
+
+
+/**
+ * Put a task that is ready for execution into the ready queue.
+ *
+ * @param task task ready for execution
+ */
+static void
+queue_ready_task (struct Task *task)
+{
+ enum GNUNET_SCHEDULER_Priority p = task->priority;
+
+ if (0 != (task->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ p = GNUNET_SCHEDULER_PRIORITY_SHUTDOWN;
+ task->next = ready[check_priority (p)];
+ ready[check_priority (p)] = task;
+ ready_count++;
+}
+
+
+/**
+ * Check which tasks are ready and move them
+ * to the respective ready queue.
+ *
+ * @param rs FDs ready for reading
+ * @param ws FDs ready for writing
+ */
+static void
+check_ready (const struct GNUNET_NETWORK_FDSet *rs,
+ const struct GNUNET_NETWORK_FDSet *ws)
+{
+ struct Task *pos;
+ struct Task *prev;
+ struct Task *next;
+ struct GNUNET_TIME_Absolute now;
+
+ now = GNUNET_TIME_absolute_get ();
+ prev = NULL;
+ pos = pending_timeout;
+ while (pos != NULL)
+ {
+ next = pos->next;
+ if (now.abs_value >= pos->timeout.abs_value)
+ pos->reason |= GNUNET_SCHEDULER_REASON_TIMEOUT;
+ if (0 == pos->reason)
+ break;
+ pending_timeout = next;
+ if (pending_timeout_last == pos)
+ pending_timeout_last = NULL;
+ queue_ready_task (pos);
+ pos = next;
+ }
+ pos = pending;
+ while (pos != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Checking readiness of task: %llu / %p\n",
+ pos->id, pos->callback_cls);
+ next = pos->next;
+ if (GNUNET_YES == is_ready (pos, now, rs, ws))
+ {
+ if (prev == NULL)
+ pending = next;
+ else
+ prev->next = next;
+ queue_ready_task (pos);
+ pos = next;
+ continue;
+ }
+ prev = pos;
+ pos = next;
+ }
+}
+
+
+/**
+ * Request the shutdown of a scheduler. Marks all currently
+ * pending tasks as ready because of shutdown. This will
+ * cause all tasks to run (as soon as possible, respecting
+ * priorities and prerequisite tasks). Note that tasks
+ * scheduled AFTER this call may still be delayed arbitrarily.
+ */
+void
+GNUNET_SCHEDULER_shutdown ()
+{
+ struct Task *pos;
+ int i;
+
+ pos = pending_timeout;
+ while (pos != NULL)
+ {
+ pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
+ /* we don't move the task into the ready queue yet; check_ready
+ * will do that later, possibly adding additional
+ * readiness-factors */
+ pos = pos->next;
+ }
+ pos = pending;
+ while (pos != NULL)
+ {
+ pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
+ /* we don't move the task into the ready queue yet; check_ready
+ * will do that later, possibly adding additional
+ * readiness-factors */
+ pos = pos->next;
+ }
+ for (i = 0; i < GNUNET_SCHEDULER_PRIORITY_COUNT; i++)
+ {
+ pos = ready[i];
+ while (pos != NULL)
+ {
+ pos->reason |= GNUNET_SCHEDULER_REASON_SHUTDOWN;
+ /* we don't move the task into the ready queue yet; check_ready
+ * will do that later, possibly adding additional
+ * readiness-factors */
+ pos = pos->next;
+ }
+ }
+}
+
+
+/**
+ * Destroy a task (release associated resources)
+ *
+ * @param t task to destroy
+ */
+static void
+destroy_task (struct Task *t)
+{
+ if (NULL != t->read_set)
+ GNUNET_NETWORK_fdset_destroy (t->read_set);
+ if (NULL != t->write_set)
+ GNUNET_NETWORK_fdset_destroy (t->write_set);
+#if EXECINFO
+ GNUNET_free (t->backtrace_strings);
+#endif
+ GNUNET_free (t);
+}
+
+
+/**
+ * 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.
+ *
+ * @param rs FDs ready for reading
+ * @param ws FDs ready for writing
+ */
+static void
+run_ready (struct GNUNET_NETWORK_FDSet *rs, struct GNUNET_NETWORK_FDSet *ws)
+{
+ enum GNUNET_SCHEDULER_Priority p;
+ struct Task *pos;
+ struct GNUNET_SCHEDULER_TaskContext tc;
+
+ max_priority_added = GNUNET_SCHEDULER_PRIORITY_KEEP;
+ do
+ {
+ if (ready_count == 0)
+ return;
+ GNUNET_assert (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 = ready[p];
+ if (pos != NULL)
+ break;
+ }
+ GNUNET_assert (pos != NULL); /* ready_count wrong? */
+ ready[p] = pos->next;
+ ready_count--;
+ if (current_priority != pos->priority)
+ {
+ current_priority = pos->priority;
+ (void) GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ pos->priority);
+ }
+ current_lifeness = pos->lifeness;
+ active_task = pos;
+#if PROFILE_DELAYS
+ if (GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value >
+ DELAY_THRESHOLD.rel_value)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Task %llu took %llums to be scheduled\n",
+ pos->id,
+ (unsigned long long)
+ GNUNET_TIME_absolute_get_duration (pos->start_time).rel_value);
+ }
+#endif
+ tc.reason = pos->reason;
+ tc.read_ready = (pos->read_set == NULL) ? rs : pos->read_set;
+ if ((pos->read_fd != -1) &&
+ (0 != (pos->reason & GNUNET_SCHEDULER_REASON_READ_READY)))
+ GNUNET_NETWORK_fdset_set_native (rs, pos->read_fd);
+ tc.write_ready = (pos->write_set == NULL) ? ws : pos->write_set;
+ if ((pos->write_fd != -1) &&
+ (0 != (pos->reason & GNUNET_SCHEDULER_REASON_WRITE_READY)))
+ GNUNET_NETWORK_fdset_set_native (ws, pos->write_fd);
+ if (((tc.reason & GNUNET_SCHEDULER_REASON_WRITE_READY) != 0) &&
+ (pos->write_fd != -1) &&
+ (!GNUNET_NETWORK_fdset_test_native (ws, pos->write_fd)))
+ GNUNET_abort (); // added to ready in previous select loop!
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Running task: %llu / %p\n", pos->id,
+ pos->callback_cls);
+ pos->callback (pos->callback_cls, &tc);
+#if EXECINFO
+ int i;
+
+ for (i = 0; i < pos->num_backtrace_strings; i++)
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Task %llu trace %d: %s\n", pos->id, i,
+ pos->backtrace_strings[i]);
+#endif
+ active_task = NULL;
+ destroy_task (pos);
+ tasks_run++;
+ }
+ while ((pending == NULL) || (p >= max_priority_added));
+}
+
+/**
+ * Pipe used to communicate shutdown via signal.
+ */
+static struct GNUNET_DISK_PipeHandle *shutdown_pipe_handle;
+
+/**
+ * Process ID of this process at the time we installed the various
+ * signal handlers.
+ */
+static pid_t my_pid;
+
+/**
+ * Signal handler called for SIGPIPE.
+ */
+#ifndef MINGW
+static void
+sighandler_pipe ()
+{
+ return;
+}
+#endif
+/**
+ * Signal handler called for signals that should cause us to shutdown.
+ */
+static void
+sighandler_shutdown ()
+{
+ static char c;
+ int old_errno = errno; /* backup errno */
+
+ if (getpid () != my_pid)
+ exit (1); /* we have fork'ed since the signal handler was created,
+ * ignore the signal, see https://gnunet.org/vfork discussion */
+ GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
+ (shutdown_pipe_handle, GNUNET_DISK_PIPE_END_WRITE),
+ &c, sizeof (c));
+ errno = old_errno;
+}
+
+
+/**
+ * Check if the system is still life. Trigger shutdown if we
+ * have tasks, but none of them give us lifeness.
+ *
+ * @return GNUNET_OK to continue the main loop,
+ * GNUNET_NO to exit
+ */
+static int
+check_lifeness ()
+{
+ struct Task *t;
+
+ if (ready_count > 0)
+ return GNUNET_OK;
+ for (t = pending; NULL != t; t = t->next)
+ if (t->lifeness == GNUNET_YES)
+ return GNUNET_OK;
+ for (t = pending_timeout; NULL != t; t = t->next)
+ if (t->lifeness == GNUNET_YES)
+ return GNUNET_OK;
+ if ((NULL != pending) || (NULL != pending_timeout))
+ {
+ GNUNET_SCHEDULER_shutdown ();
+ return GNUNET_OK;
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Initialize and run scheduler. This function will return when all
+ * tasks have completed. On systems with signals, receiving a SIGTERM
+ * (and other similar signals) will cause "GNUNET_SCHEDULER_shutdown"
+ * to be run after the active task is complete. As a result, SIGTERM
+ * causes all active tasks to be scheduled with reason
+ * "GNUNET_SCHEDULER_REASON_SHUTDOWN". (However, tasks added
+ * afterwards will execute normally!). Note that any particular signal
+ * will only shut down one scheduler; applications should always only
+ * create a single scheduler.
+ *
+ * @param task task to run immediately
+ * @param task_cls closure of task
+ */
+void
+GNUNET_SCHEDULER_run (GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ struct GNUNET_NETWORK_FDSet *rs;
+ struct GNUNET_NETWORK_FDSet *ws;
+ struct GNUNET_TIME_Relative timeout;
+ int ret;
+ struct GNUNET_SIGNAL_Context *shc_int;
+ struct GNUNET_SIGNAL_Context *shc_term;
+
+#ifndef MINGW
+ struct GNUNET_SIGNAL_Context *shc_quit;
+ struct GNUNET_SIGNAL_Context *shc_hup;
+ struct GNUNET_SIGNAL_Context *shc_pipe;
+#endif
+ unsigned long long last_tr;
+ unsigned int busy_wait_warning;
+ const struct GNUNET_DISK_FileHandle *pr;
+ char c;
+
+ GNUNET_assert (active_task == NULL);
+ rs = GNUNET_NETWORK_fdset_create ();
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_assert (shutdown_pipe_handle == NULL);
+ shutdown_pipe_handle = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
+ GNUNET_assert (shutdown_pipe_handle != NULL);
+ pr = GNUNET_DISK_pipe_handle (shutdown_pipe_handle,
+ GNUNET_DISK_PIPE_END_READ);
+ GNUNET_assert (pr != NULL);
+ my_pid = getpid ();
+ shc_int = GNUNET_SIGNAL_handler_install (SIGINT, &sighandler_shutdown);
+ shc_term = GNUNET_SIGNAL_handler_install (SIGTERM, &sighandler_shutdown);
+#ifndef MINGW
+ shc_pipe = GNUNET_SIGNAL_handler_install (SIGPIPE, &sighandler_pipe);
+ shc_quit = GNUNET_SIGNAL_handler_install (SIGQUIT, &sighandler_shutdown);
+ shc_hup = GNUNET_SIGNAL_handler_install (SIGHUP, &sighandler_shutdown);
+#endif
+ current_priority = GNUNET_SCHEDULER_PRIORITY_DEFAULT;
+ current_lifeness = GNUNET_YES;
+ GNUNET_SCHEDULER_add_continuation (task, task_cls,
+ GNUNET_SCHEDULER_REASON_STARTUP);
+ active_task = (void *) (long) -1; /* force passing of sanity check */
+ GNUNET_SCHEDULER_add_now_with_lifeness (GNUNET_NO,
+ &GNUNET_OS_install_parent_control_handler,
+ NULL);
+ active_task = NULL;
+ last_tr = 0;
+ busy_wait_warning = 0;
+ while (GNUNET_OK == check_lifeness ())
+ {
+ GNUNET_NETWORK_fdset_zero (rs);
+ GNUNET_NETWORK_fdset_zero (ws);
+ timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+ update_sets (rs, ws, &timeout);
+ GNUNET_NETWORK_fdset_handle_set (rs, pr);
+ if (ready_count > 0)
+ {
+ /* no blocking, more work already ready! */
+ timeout = GNUNET_TIME_UNIT_ZERO;
+ }
+ if (NULL == scheduler_select)
+ ret = GNUNET_NETWORK_socket_select (rs, ws, NULL, timeout);
+ else
+ ret = scheduler_select (scheduler_select_cls, rs, ws, NULL, timeout);
+ if (ret == GNUNET_SYSERR)
+ {
+ if (errno == EINTR)
+ continue;
+
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "select");
+#ifndef MINGW
+#if USE_LSOF
+ char lsof[512];
+
+ snprintf (lsof, sizeof (lsof), "lsof -p %d", getpid ());
+ (void) close (1);
+ (void) dup2 (2, 1);
+ if (0 != system (lsof))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "system");
+#endif
+#endif
+ GNUNET_abort ();
+ break;
+ }
+ if ((ret == 0) && (timeout.rel_value == 0) && (busy_wait_warning > 16))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("Looks like we're busy waiting...\n"));
+ sleep (1); /* mitigate */
+ }
+ check_ready (rs, ws);
+ run_ready (rs, ws);
+ if (GNUNET_NETWORK_fdset_handle_isset (rs, pr))
+ {
+ /* consume the signal */
+ GNUNET_DISK_file_read (pr, &c, sizeof (c));
+ /* mark all active tasks as ready due to shutdown */
+ GNUNET_SCHEDULER_shutdown ();
+ }
+ if (last_tr == tasks_run)
+ {
+ busy_wait_warning++;
+ }
+ else
+ {
+ last_tr = tasks_run;
+ busy_wait_warning = 0;
+ }
+ }
+ GNUNET_SIGNAL_handler_uninstall (shc_int);
+ GNUNET_SIGNAL_handler_uninstall (shc_term);
+#ifndef MINGW
+ GNUNET_SIGNAL_handler_uninstall (shc_pipe);
+ GNUNET_SIGNAL_handler_uninstall (shc_quit);
+ GNUNET_SIGNAL_handler_uninstall (shc_hup);
+#endif
+ GNUNET_DISK_pipe_close (shutdown_pipe_handle);
+ shutdown_pipe_handle = NULL;
+ GNUNET_NETWORK_fdset_destroy (rs);
+ GNUNET_NETWORK_fdset_destroy (ws);
+}
+
+
+/**
+ * Obtain the reason code for why the current task was
+ * started. Will return the same value as
+ * the GNUNET_SCHEDULER_TaskContext's reason field.
+ *
+ * @return reason(s) why the current task is run
+ */
+enum GNUNET_SCHEDULER_Reason
+GNUNET_SCHEDULER_get_reason ()
+{
+ GNUNET_assert (active_task != NULL);
+ return active_task->reason;
+}
+
+
+/**
+ * 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 p priority level to look at
+ * @return number of tasks pending right now
+ */
+unsigned int
+GNUNET_SCHEDULER_get_load (enum GNUNET_SCHEDULER_Priority p)
+{
+ struct Task *pos;
+ unsigned int ret;
+
+ GNUNET_assert (active_task != NULL);
+ if (p == GNUNET_SCHEDULER_PRIORITY_COUNT)
+ return ready_count;
+ if (p == GNUNET_SCHEDULER_PRIORITY_KEEP)
+ p = current_priority;
+ ret = 0;
+ pos = ready[check_priority (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 task id of the task to cancel
+ * @return original closure of the task
+ */
+void *
+GNUNET_SCHEDULER_cancel (GNUNET_SCHEDULER_TaskIdentifier task)
+{
+ struct Task *t;
+ struct Task *prev;
+ enum GNUNET_SCHEDULER_Priority p;
+ int to;
+ void *ret;
+
+ GNUNET_assert (active_task != NULL);
+ to = 0;
+ prev = NULL;
+ t = pending;
+ while (t != NULL)
+ {
+ if (t->id == task)
+ break;
+ prev = t;
+ t = t->next;
+ }
+ if (t == NULL)
+ {
+ prev = NULL;
+ to = 1;
+ t = pending_timeout;
+ while (t != NULL)
+ {
+ if (t->id == task)
+ break;
+ prev = t;
+ t = t->next;
+ }
+ if (pending_timeout_last == t)
+ pending_timeout_last = NULL;
+ }
+ p = 0;
+ while (t == NULL)
+ {
+ p++;
+ if (p >= GNUNET_SCHEDULER_PRIORITY_COUNT)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Attempt to cancel dead task %llu!\n"),
+ (unsigned long long) task);
+ GNUNET_assert (0);
+ }
+ prev = NULL;
+ t = ready[p];
+ while (t != NULL)
+ {
+ if (t->id == task)
+ {
+ ready_count--;
+ break;
+ }
+ prev = t;
+ t = t->next;
+ }
+ }
+ if (prev == NULL)
+ {
+ if (p == 0)
+ {
+ if (to == 0)
+ {
+ pending = t->next;
+ }
+ else
+ {
+ pending_timeout = t->next;
+ }
+ }
+ else
+ {
+ ready[p] = t->next;
+ }
+ }
+ else
+ {
+ prev->next = t->next;
+ }
+ ret = t->callback_cls;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Canceling task: %llu / %p\n", task,
+ t->callback_cls);
+ destroy_task (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 task main function of the task
+ * @param task_cls closure for 'main'
+ * @param reason reason for task invocation
+ * @param priority priority to use for the task
+ */
+void
+GNUNET_SCHEDULER_add_continuation_with_priority (GNUNET_SCHEDULER_Task task, void *task_cls,
+ enum GNUNET_SCHEDULER_Reason reason,
+ enum GNUNET_SCHEDULER_Priority priority)
+{
+ struct Task *t;
+
+#if EXECINFO
+ void *backtrace_array[50];
+#endif
+
+ GNUNET_assert (NULL != task);
+ GNUNET_assert ((active_task != NULL) ||
+ (reason == GNUNET_SCHEDULER_REASON_STARTUP));
+ t = GNUNET_malloc (sizeof (struct Task));
+#if EXECINFO
+ t->num_backtrace_strings = backtrace (backtrace_array, 50);
+ t->backtrace_strings =
+ backtrace_symbols (backtrace_array, t->num_backtrace_strings);
+#endif
+ t->read_fd = -1;
+ t->write_fd = -1;
+ t->callback = task;
+ t->callback_cls = task_cls;
+ t->id = ++last_id;
+#if PROFILE_DELAYS
+ t->start_time = GNUNET_TIME_absolute_get ();
+#endif
+ t->reason = reason;
+ t->priority = priority;
+ t->lifeness = current_lifeness;
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding continuation task: %llu / %p\n", t->id,
+ t->callback_cls);
+ queue_ready_task (t);
+}
+
+
+/**
+ * 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 task main function of the task
+ * @param task_cls closure for 'main'
+ * @param reason reason for task invocation
+ */
+void
+GNUNET_SCHEDULER_add_continuation (GNUNET_SCHEDULER_Task task, void *task_cls,
+ enum GNUNET_SCHEDULER_Reason reason)
+{
+ GNUNET_SCHEDULER_add_continuation_with_priority (task, task_cls,
+ reason,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT);
+}
+
+
+/**
+ * Schedule a new task to be run after the specified prerequisite task
+ * has completed. It will be run with the DEFAULT priority.
+ *
+ * @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-readiness
+ * are satisfied). Use GNUNET_SCHEDULER_NO_TASK to not have any dependency
+ * on completion of other tasks (this will cause the task to run as
+ * soon as possible).
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ return GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ prerequisite_task, GNUNET_TIME_UNIT_ZERO,
+ NULL, NULL, task, task_cls);
+}
+
+
+/**
+ * Schedule a new task to be run with a specified priority.
+ *
+ * @param prio how important is the new task?
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_with_priority (enum GNUNET_SCHEDULER_Priority prio,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ return GNUNET_SCHEDULER_add_select (prio, GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_UNIT_ZERO, NULL, NULL, task,
+ task_cls);
+}
+
+
+
+/**
+ * Schedule a new task to be run with a specified delay. The task
+ * will be scheduled for execution once the delay has expired.
+ *
+ * @param delay when should this operation time out? Use
+ * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param priority priority to use for the task
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_delayed_with_priority (struct GNUNET_TIME_Relative delay,
+ enum GNUNET_SCHEDULER_Priority priority,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ struct Task *t;
+ struct Task *pos;
+ struct Task *prev;
+
+#if EXECINFO
+ void *backtrace_array[MAX_TRACE_DEPTH];
+#endif
+
+ GNUNET_assert (active_task != NULL);
+ GNUNET_assert (NULL != task);
+ t = GNUNET_malloc (sizeof (struct Task));
+ t->callback = task;
+ t->callback_cls = task_cls;
+#if EXECINFO
+ t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
+ t->backtrace_strings =
+ backtrace_symbols (backtrace_array, t->num_backtrace_strings);
+#endif
+ t->read_fd = -1;
+ t->write_fd = -1;
+ t->id = ++last_id;
+#if PROFILE_DELAYS
+ t->start_time = GNUNET_TIME_absolute_get ();
+#endif
+ t->timeout = GNUNET_TIME_relative_to_absolute (delay);
+ t->priority = priority;
+ t->lifeness = current_lifeness;
+ /* try tail first (optimization in case we are
+ * appending to a long list of tasks with timeouts) */
+ prev = pending_timeout_last;
+ if (prev != NULL)
+ {
+ if (prev->timeout.abs_value > t->timeout.abs_value)
+ prev = NULL;
+ else
+ pos = prev->next; /* heuristic success! */
+ }
+ if (prev == NULL)
+ {
+ /* heuristic failed, do traversal of timeout list */
+ pos = pending_timeout;
+ }
+ while ((pos != NULL) &&
+ ((pos->timeout.abs_value <= t->timeout.abs_value) ||
+ (pos->reason != 0)))
+ {
+ prev = pos;
+ pos = pos->next;
+ }
+ if (prev == NULL)
+ pending_timeout = t;
+ else
+ prev->next = t;
+ t->next = pos;
+ /* hyper-optimization... */
+ pending_timeout_last = t;
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding task: %llu / %p\n", t->id,
+ t->callback_cls);
+#if EXECINFO
+ int i;
+
+ for (i = 0; i < t->num_backtrace_strings; i++)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Task %llu trace %d: %s\n", t->id, i,
+ t->backtrace_strings[i]);
+#endif
+ return t->id;
+}
+
+
+/**
+ * Schedule a new task to be run with a specified delay. The task
+ * will be scheduled for execution once the delay has expired. It
+ * will be run with the DEFAULT priority.
+ *
+ * @param delay when should this operation time out? Use
+ * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_delayed (struct GNUNET_TIME_Relative delay,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ return GNUNET_SCHEDULER_add_delayed_with_priority (delay,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ task, task_cls);
+}
+
+
+/**
+ * Schedule a new task to be run as soon as possible. The task
+ * will be run with the DEFAULT priority.
+ *
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_now (GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ return GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_ZERO, task, task_cls);
+}
+
+
+/**
+ * Schedule a new task to be run as soon as possible with the
+ * (transitive) ignore-shutdown flag either explicitly set or
+ * explicitly enabled. This task (and all tasks created from it,
+ * other than by another call to this function) will either count or
+ * not count for the 'lifeness' of the process. This API is only
+ * useful in a few special cases.
+ *
+ * @param lifeness GNUNET_YES if the task counts for lifeness, GNUNET_NO if not.
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_now_with_lifeness (int lifeness,
+ GNUNET_SCHEDULER_Task task,
+ void *task_cls)
+{
+ GNUNET_SCHEDULER_TaskIdentifier ret;
+
+ ret =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_UNIT_ZERO, NULL, NULL, task,
+ task_cls);
+ GNUNET_assert (pending->id == ret);
+ pending->lifeness = lifeness;
+ return ret;
+}
+
+
+/**
+ * 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
+ * <code>
+ * (prerequisite-run)
+ * && (delay-ready
+ * || any-rs-ready
+ * || any-ws-ready
+ * || shutdown-active )
+ * </code>
+ *
+ * @param delay how long should we wait? Use GNUNET_TIME_UNIT_FOREVER_REL for "forever",
+ * which means that the task will only be run after we receive SIGTERM
+ * @param priority priority to use
+ * @param rfd file descriptor we want to read (can be -1)
+ * @param wfd file descriptors we want to write (can be -1)
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+#ifndef MINGW
+static GNUNET_SCHEDULER_TaskIdentifier
+add_without_sets (struct GNUNET_TIME_Relative delay,
+ enum GNUNET_SCHEDULER_Priority priority,
+ int rfd, int wfd,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ struct Task *t;
+
+#if EXECINFO
+ void *backtrace_array[MAX_TRACE_DEPTH];
+#endif
+
+ GNUNET_assert (active_task != NULL);
+ GNUNET_assert (NULL != task);
+ t = GNUNET_malloc (sizeof (struct Task));
+ t->callback = task;
+ t->callback_cls = task_cls;
+#if EXECINFO
+ t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
+ t->backtrace_strings =
+ backtrace_symbols (backtrace_array, t->num_backtrace_strings);
+#endif
+#if DEBUG_FDS
+ if (-1 != rfd)
+ {
+ int flags = fcntl (rfd, F_GETFD);
+
+ if ((flags == -1) && (errno == EBADF))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", rfd);
+#if EXECINFO
+ int i;
+
+ for (i = 0; i < t->num_backtrace_strings; i++)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Trace: %s\n", t->backtrace_strings[i]);
+#endif
+ GNUNET_assert (0);
+ }
+ }
+ if (-1 != wfd)
+ {
+ int flags = fcntl (wfd, F_GETFD);
+
+ if (flags == -1 && errno == EBADF)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, "Got invalid file descriptor %d!\n", wfd);
+#if EXECINFO
+ int i;
+
+ for (i = 0; i < t->num_backtrace_strings; i++)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Trace: %s\n", t->backtrace_strings[i]);
+#endif
+ GNUNET_assert (0);
+ }
+ }
+#endif
+ t->read_fd = rfd;
+ GNUNET_assert (wfd >= -1);
+ t->write_fd = wfd;
+ t->id = ++last_id;
+#if PROFILE_DELAYS
+ t->start_time = GNUNET_TIME_absolute_get ();
+#endif
+ t->prereq_id = GNUNET_SCHEDULER_NO_TASK;
+ t->timeout = GNUNET_TIME_relative_to_absolute (delay);
+ t->priority = check_priority ((priority == GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority : priority);
+ t->lifeness = current_lifeness;
+ t->next = pending;
+ pending = t;
+ max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding task: %llu / %p\n", t->id,
+ t->callback_cls);
+#if EXECINFO
+ int i;
+
+ for (i = 0; i < t->num_backtrace_strings; i++)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Task %llu trace %d: %s\n", t->id, i,
+ t->backtrace_strings[i]);
+#endif
+ return t->id;
+}
+#endif
+
+
+
+/**
+ * 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. It will be run with the DEFAULT priority.
+ *
+ * @param delay when should this operation time out? Use
+ * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param rfd read file-descriptor
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_read_net (struct GNUNET_TIME_Relative delay,
+ struct GNUNET_NETWORK_Handle *rfd,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+#if MINGW
+ struct GNUNET_NETWORK_FDSet *rs;
+ GNUNET_SCHEDULER_TaskIdentifier ret;
+
+ GNUNET_assert (rfd != NULL);
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_set (rs, rfd);
+ ret =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK, delay, rs, NULL,
+ task, task_cls);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ return ret;
+#else
+ return add_without_sets (delay,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_NETWORK_get_fd (rfd), -1, task,
+ task_cls);
+#endif
+}
+
+
+/**
+ * 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. It will be run with the priority of
+ * the calling task.
+ *
+ * @param delay when should this operation time out? Use
+ * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param wfd write file-descriptor
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_write_net (struct GNUNET_TIME_Relative delay,
+ struct GNUNET_NETWORK_Handle *wfd,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+#if MINGW
+ struct GNUNET_NETWORK_FDSet *ws;
+ GNUNET_SCHEDULER_TaskIdentifier ret;
+
+ GNUNET_assert (wfd != NULL);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_set (ws, wfd);
+ ret =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK, delay, NULL, ws,
+ task, task_cls);
+ GNUNET_NETWORK_fdset_destroy (ws);
+ return ret;
+#else
+ GNUNET_assert (GNUNET_NETWORK_get_fd (wfd) >= 0);
+ return add_without_sets (delay,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ -1, GNUNET_NETWORK_get_fd (wfd), task,
+ task_cls);
+#endif
+}
+
+
+/**
+ * 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. It will be run with the DEFAULT priority.
+ *
+ * @param delay when should this operation time out? Use
+ * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param rfd read file-descriptor
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_read_file (struct GNUNET_TIME_Relative delay,
+ const struct GNUNET_DISK_FileHandle *rfd,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+#if MINGW
+ struct GNUNET_NETWORK_FDSet *rs;
+ GNUNET_SCHEDULER_TaskIdentifier ret;
+
+ GNUNET_assert (rfd != NULL);
+ rs = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_handle_set (rs, rfd);
+ ret =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK, delay, rs, NULL,
+ task, task_cls);
+ GNUNET_NETWORK_fdset_destroy (rs);
+ return ret;
+#else
+ int fd;
+
+ GNUNET_DISK_internal_file_handle_ (rfd, &fd, sizeof (int));
+ return add_without_sets (delay,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ fd, -1, task, task_cls);
+
+#endif
+}
+
+
+/**
+ * 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. It will be run with the DEFAULT priority.
+ *
+ * @param delay when should this operation time out? Use
+ * GNUNET_TIME_UNIT_FOREVER_REL for "on shutdown"
+ * @param wfd write file-descriptor
+ * @param task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_write_file (struct GNUNET_TIME_Relative delay,
+ const struct GNUNET_DISK_FileHandle *wfd,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+#if MINGW
+ struct GNUNET_NETWORK_FDSet *ws;
+ GNUNET_SCHEDULER_TaskIdentifier ret;
+
+ GNUNET_assert (wfd != NULL);
+ ws = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_handle_set (ws, wfd);
+ ret =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ GNUNET_SCHEDULER_NO_TASK, delay, NULL, ws,
+ task, task_cls);
+ GNUNET_NETWORK_fdset_destroy (ws);
+ return ret;
+#else
+ int fd;
+
+ GNUNET_DISK_internal_file_handle_ (wfd, &fd, sizeof (int));
+ GNUNET_assert (fd >= 0);
+ return add_without_sets (delay,
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ -1, fd, task, task_cls);
+
+#endif
+}
+
+
+
+/**
+ * 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
+ * <code>
+ * (prerequisite-run)
+ * && (delay-ready
+ * || any-rs-ready
+ * || any-ws-ready
+ * || (shutdown-active && run-on-shutdown) )
+ * </code>
+ *
+ * @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-readiness
+ * are satisfied). Use GNUNET_SCHEDULER_NO_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",
+ * which means that the task will only be run after we receive SIGTERM
+ * @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 task main function of the task
+ * @param task_cls closure of task
+ * @return unique task identifier for the job
+ * only valid until "task" is started!
+ */
+GNUNET_SCHEDULER_TaskIdentifier
+GNUNET_SCHEDULER_add_select (enum GNUNET_SCHEDULER_Priority prio,
+ GNUNET_SCHEDULER_TaskIdentifier prerequisite_task,
+ struct GNUNET_TIME_Relative delay,
+ const struct GNUNET_NETWORK_FDSet *rs,
+ const struct GNUNET_NETWORK_FDSet *ws,
+ GNUNET_SCHEDULER_Task task, void *task_cls)
+{
+ struct Task *t;
+
+#if EXECINFO
+ void *backtrace_array[MAX_TRACE_DEPTH];
+#endif
+
+ GNUNET_assert (active_task != NULL);
+ GNUNET_assert (NULL != task);
+ t = GNUNET_malloc (sizeof (struct Task));
+ t->callback = task;
+ t->callback_cls = task_cls;
+#if EXECINFO
+ t->num_backtrace_strings = backtrace (backtrace_array, MAX_TRACE_DEPTH);
+ t->backtrace_strings =
+ backtrace_symbols (backtrace_array, t->num_backtrace_strings);
+#endif
+ t->read_fd = -1;
+ t->write_fd = -1;
+ if (rs != NULL)
+ {
+ t->read_set = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy (t->read_set, rs);
+ }
+ if (ws != NULL)
+ {
+ t->write_set = GNUNET_NETWORK_fdset_create ();
+ GNUNET_NETWORK_fdset_copy (t->write_set, ws);
+ }
+ t->id = ++last_id;
+#if PROFILE_DELAYS
+ t->start_time = GNUNET_TIME_absolute_get ();
+#endif
+ t->prereq_id = prerequisite_task;
+ t->timeout = GNUNET_TIME_relative_to_absolute (delay);
+ t->priority =
+ check_priority ((prio ==
+ GNUNET_SCHEDULER_PRIORITY_KEEP) ? current_priority :
+ prio);
+ t->lifeness = current_lifeness;
+ t->next = pending;
+ pending = t;
+ max_priority_added = GNUNET_MAX (max_priority_added, t->priority);
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Adding task: %llu / %p\n", t->id,
+ t->callback_cls);
+#if EXECINFO
+ int i;
+
+ for (i = 0; i < t->num_backtrace_strings; i++)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Task %llu trace %d: %s\n", t->id, i,
+ t->backtrace_strings[i]);
+#endif
+ return t->id;
+}
+
+/* end of scheduler.c */
diff --git a/src/util/server.c b/src/util/server.c
new file mode 100644
index 0000000..24804d2
--- /dev/null
+++ b/src/util/server.c
@@ -0,0 +1,1415 @@
+/*
+ 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
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_time_lib.h"
+#include "gnunet_disk_lib.h"
+#include "gnunet_protocols.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+#define DEBUG_SERVER GNUNET_EXTRA_LOGGING
+
+/**
+ * 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
+{
+ /**
+ * 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_CONNECTION_AccessCheck access;
+
+ /**
+ * Closure for access.
+ */
+ void *access_cls;
+
+ /**
+ * NULL-terminated array of sockets used to listen for new
+ * connections.
+ */
+ struct GNUNET_NETWORK_Handle **listen_sockets;
+
+ /**
+ * After how long should an idle connection time
+ * out (on write).
+ */
+ struct GNUNET_TIME_Relative idle_timeout;
+
+ /**
+ * Task scheduled to do the listening.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier listen_task;
+
+ /**
+ * 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;
+
+ /**
+ * Should all of the clients of this server continue to process
+ * connections as usual even if we get a shutdown request? (the
+ * listen socket always ignores shutdown).
+ */
+ int clients_ignore_shutdown;
+
+ GNUNET_SERVER_MstCreateCallback mst_create;
+ GNUNET_SERVER_MstDestroyCallback mst_destroy;
+ GNUNET_SERVER_MstReceiveCallback mst_receive;
+ void *mst_cls;
+};
+
+
+/**
+ * @brief handle for a client of the server
+ */
+struct GNUNET_SERVER_Client
+{
+
+ /**
+ * This is a linked list.
+ */
+ struct GNUNET_SERVER_Client *next;
+
+ /**
+ * Processing of incoming data.
+ */
+ void *mst;
+
+ /**
+ * Server that this client belongs to.
+ */
+ struct GNUNET_SERVER_Handle *server;
+
+ /**
+ * Client closure for callbacks.
+ */
+ struct GNUNET_CONNECTION_Handle *connection;
+
+ /**
+ * ID of task used to restart processing.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier restart_task;
+
+ /**
+ * Task that warns about missing calls to 'GNUNET_SERVER_receive_done'.
+ */
+ GNUNET_SCHEDULER_TaskIdentifier warn_task;
+
+ /**
+ * Time when the warn task was started.
+ */
+ struct GNUNET_TIME_Absolute warn_start;
+
+ /**
+ * Last activity on this socket (used to time it out
+ * if reference_count == 0).
+ */
+ struct GNUNET_TIME_Absolute last_activity;
+
+ /**
+ *
+ */
+ GNUNET_CONNECTION_TransmitReadyNotify callback;
+
+ /**
+ * callback
+ */
+ void *callback_cls;
+
+ /**
+ * After how long should an idle connection time
+ * out (on write).
+ */
+ struct GNUNET_TIME_Relative idle_timeout;
+
+ /**
+ * 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;
+
+ /**
+ * Are we currently trying to receive? (YES if we are, NO if we are not,
+ * SYSERR if data is already available in MST).
+ */
+ int receive_pending;
+
+ /**
+ * Finish pending write when disconnecting?
+ */
+ int finish_pending_write;
+
+ /**
+ * Persist the file handle for this client no matter what happens,
+ * force the OS to close once the process actually dies. Should only
+ * be used in special cases!
+ */
+ int persist;
+
+ /**
+ * Type of last message processed (for warn_no_receive_done).
+ */
+ uint16_t warn_type;
+};
+
+
+/**
+ * Scheduler says our listen socket is ready. Process it!
+ *
+ * @param cls handle to our server for which we are processing the listen
+ * socket
+ * @param tc reason why we are running right now
+ */
+static void
+process_listen_socket (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_SERVER_Handle *server = cls;
+ struct GNUNET_CONNECTION_Handle *sock;
+ struct GNUNET_SERVER_Client *client;
+ struct GNUNET_NETWORK_FDSet *r;
+ unsigned int i;
+
+ server->listen_task = GNUNET_SCHEDULER_NO_TASK;
+ r = GNUNET_NETWORK_fdset_create ();
+ i = 0;
+ while (NULL != server->listen_sockets[i])
+ GNUNET_NETWORK_fdset_set (r, server->listen_sockets[i++]);
+ if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
+ {
+ /* ignore shutdown, someone else will take care of it! */
+ server->listen_task =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
+ &process_listen_socket, server);
+ GNUNET_NETWORK_fdset_destroy (r);
+ return;
+ }
+ i = 0;
+ while (NULL != server->listen_sockets[i])
+ {
+ if (GNUNET_NETWORK_fdset_isset (tc->read_ready, server->listen_sockets[i]))
+ {
+ sock =
+ GNUNET_CONNECTION_create_from_accept (server->access,
+ server->access_cls,
+ server->listen_sockets[i]);
+ if (sock != NULL)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Server accepted incoming connection.\n");
+#endif
+ client = GNUNET_SERVER_connect_socket (server, sock);
+ GNUNET_CONNECTION_ignore_shutdown (sock,
+ server->clients_ignore_shutdown);
+ /* decrement reference count, we don't keep "client" alive */
+ GNUNET_SERVER_client_drop (client);
+ }
+ }
+ i++;
+ }
+ /* listen for more! */
+ server->listen_task =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
+ &process_listen_socket, server);
+ GNUNET_NETWORK_fdset_destroy (r);
+}
+
+
+/**
+ * Create and initialize a listen socket for the server.
+ *
+ * @param serverAddr address to listen on
+ * @param socklen length of address
+ * @return NULL on error, otherwise the listen socket
+ */
+static struct GNUNET_NETWORK_Handle *
+open_listen_socket (const struct sockaddr *serverAddr, socklen_t socklen)
+{
+ const static int on = 1;
+ struct GNUNET_NETWORK_Handle *sock;
+ uint16_t port;
+ int eno;
+
+ 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;
+ case AF_UNIX:
+ port = 0;
+ break;
+ default:
+ GNUNET_break (0);
+ port = 0;
+ break;
+ }
+ sock = GNUNET_NETWORK_socket_create (serverAddr->sa_family, SOCK_STREAM, 0);
+ if (NULL == sock)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
+ errno = 0;
+ return NULL;
+ }
+ if (port != 0)
+ {
+ if (GNUNET_NETWORK_socket_setsockopt
+ (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "setsockopt");
+#ifdef IPV6_V6ONLY
+ if ((serverAddr->sa_family == AF_INET6) &&
+ (GNUNET_NETWORK_socket_setsockopt
+ (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "setsockopt");
+#endif
+ }
+ /* bind the socket */
+ if (GNUNET_NETWORK_socket_bind (sock, serverAddr, socklen) != GNUNET_OK)
+ {
+ eno = errno;
+ if (errno != EADDRINUSE)
+ {
+ /* we don't log 'EADDRINUSE' here since an IPv4 bind may
+ * fail if we already took the port on IPv6; if both IPv4 and
+ * IPv6 binds fail, then our caller will log using the
+ * errno preserved in 'eno' */
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
+ if (port != 0)
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("`%s' failed for port %d (%s).\n"),
+ "bind", port,
+ (serverAddr->sa_family == AF_INET) ? "IPv4" : "IPv6");
+ eno = 0;
+ }
+ else
+ {
+ if (port != 0)
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("`%s' failed for port %d (%s): address already in use\n"),
+ "bind", port,
+ (serverAddr->sa_family == AF_INET) ? "IPv4" : "IPv6");
+ else if (serverAddr->sa_family == AF_UNIX)
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("`%s' failed for `%s': address already in use\n"), "bind",
+ ((const struct sockaddr_un *) serverAddr)->sun_path);
+
+ }
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+ errno = eno;
+ return NULL;
+ }
+ if (GNUNET_OK != GNUNET_NETWORK_socket_listen (sock, 5))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "listen");
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock));
+ errno = 0;
+ return NULL;
+ }
+#if DEBUG_SERVER
+ if (port != 0)
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Server starts to listen on port %u.\n",
+ port);
+#endif
+ return sock;
+}
+
+
+/**
+ * Create a new server.
+ *
+ * @param access function for access control
+ * @param access_cls closure for access
+ * @param lsocks NULL-terminated array of listen 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_with_sockets (GNUNET_CONNECTION_AccessCheck access,
+ void *access_cls,
+ struct GNUNET_NETWORK_Handle **lsocks,
+ struct GNUNET_TIME_Relative idle_timeout,
+ int require_found)
+{
+ struct GNUNET_SERVER_Handle *ret;
+ struct GNUNET_NETWORK_FDSet *r;
+ int i;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Handle));
+ ret->idle_timeout = idle_timeout;
+ ret->listen_sockets = lsocks;
+ ret->access = access;
+ ret->access_cls = access_cls;
+ ret->require_found = require_found;
+ if (lsocks != NULL)
+ {
+ r = GNUNET_NETWORK_fdset_create ();
+ i = 0;
+ while (NULL != ret->listen_sockets[i])
+ GNUNET_NETWORK_fdset_set (r, ret->listen_sockets[i++]);
+ ret->listen_task =
+ GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
+ GNUNET_SCHEDULER_NO_TASK,
+ GNUNET_TIME_UNIT_FOREVER_REL, r, NULL,
+ &process_listen_socket, ret);
+ GNUNET_NETWORK_fdset_destroy (r);
+ }
+ return ret;
+}
+
+
+/**
+ * Create a new server.
+ *
+ * @param access function for access control
+ * @param access_cls closure for access
+ * @param serverAddr address to listen on (including port), NULL terminated array
+ * @param socklen length of serverAddr
+ * @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 (GNUNET_CONNECTION_AccessCheck access, void *access_cls,
+ struct sockaddr *const *serverAddr,
+ const socklen_t * socklen,
+ struct GNUNET_TIME_Relative idle_timeout,
+ int require_found)
+{
+ struct GNUNET_NETWORK_Handle **lsocks;
+ unsigned int i;
+ unsigned int j;
+
+ i = 0;
+ while (serverAddr[i] != NULL)
+ i++;
+ if (i > 0)
+ {
+ lsocks = GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (i + 1));
+ i = 0;
+ j = 0;
+ while (serverAddr[i] != NULL)
+ {
+ lsocks[j] = open_listen_socket (serverAddr[i], socklen[i]);
+ if (lsocks[j] != NULL)
+ j++;
+ i++;
+ }
+ if (j == 0)
+ {
+ if (errno != 0)
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "bind");
+ GNUNET_free (lsocks);
+ lsocks = NULL;
+ }
+ }
+ else
+ {
+ lsocks = NULL;
+ }
+ return GNUNET_SERVER_create_with_sockets (access, access_cls, lsocks,
+ idle_timeout, require_found);
+}
+
+
+/**
+ * Free resources held by this server.
+ *
+ * @param s server to destroy
+ */
+void
+GNUNET_SERVER_destroy (struct GNUNET_SERVER_Handle *s)
+{
+ struct HandlerList *hpos;
+ struct NotifyList *npos;
+ unsigned int i;
+
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Server shutting down.\n");
+#endif
+ if (GNUNET_SCHEDULER_NO_TASK != s->listen_task)
+ {
+ GNUNET_SCHEDULER_cancel (s->listen_task);
+ s->listen_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (s->listen_sockets != NULL)
+ {
+ i = 0;
+ while (s->listen_sockets[i] != NULL)
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_NETWORK_socket_close (s->listen_sockets[i++]));
+ GNUNET_free (s->listen_sockets);
+ s->listen_sockets = NULL;
+ }
+ while (s->clients != NULL)
+ GNUNET_SERVER_client_disconnect (s->clients);
+ while (NULL != (hpos = s->handlers))
+ {
+ s->handlers = hpos->next;
+ GNUNET_free (hpos);
+ }
+ while (NULL != (npos = s->disconnect_notify_list))
+ {
+ npos->callback (npos->callback_cls, NULL);
+ s->disconnect_notify_list = npos->next;
+ GNUNET_free (npos);
+ }
+ GNUNET_free (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)
+{
+ struct HandlerList *p;
+
+ p = GNUNET_malloc (sizeof (struct HandlerList));
+ p->handlers = handlers;
+ p->next = server->handlers;
+ server->handlers = p;
+}
+
+
+void
+GNUNET_SERVER_set_callbacks (struct GNUNET_SERVER_Handle *server,
+ GNUNET_SERVER_MstCreateCallback create,
+ GNUNET_SERVER_MstDestroyCallback destroy,
+ GNUNET_SERVER_MstReceiveCallback receive,
+ void *cls)
+{
+ server->mst_create = create;
+ server->mst_destroy = destroy;
+ server->mst_receive = receive;
+ server->mst_cls = cls;
+}
+
+
+/**
+ * Task run to warn about missing calls to 'GNUNET_SERVER_receive_done'.
+ *
+ * @param cls our 'struct GNUNET_SERVER_Client*' to process more requests from
+ * @param tc scheduler context (unused)
+ */
+static void
+warn_no_receive_done (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+
+ client->warn_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
+ &warn_no_receive_done, client);
+ if (0 == (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _
+ ("Processing code for message of type %u did not call GNUNET_SERVER_receive_done after %llums\n"),
+ (unsigned int) client->warn_type,
+ (unsigned long long)
+ GNUNET_TIME_absolute_get_duration (client->warn_start).rel_value);
+}
+
+
+/**
+ * Disable the warning the server issues if a message is not acknowledged
+ * in a timely fashion. Use this call if a client is intentionally delayed
+ * for a while. Only applies to the current message.
+ *
+ * @param client client for which to disable the warning
+ */
+void
+GNUNET_SERVER_disable_receive_done_warning (struct GNUNET_SERVER_Client *client)
+{
+ if (GNUNET_SCHEDULER_NO_TASK != client->warn_task)
+ {
+ GNUNET_SCHEDULER_cancel (client->warn_task);
+ client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+}
+
+
+/**
+ * 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);
+#if DEBUG_SERVER
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server schedules transmission of %u-byte message of type %u to client.\n",
+ size, type);
+#endif
+ 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) || (mh->type == GNUNET_MESSAGE_TYPE_ALL))
+ {
+ if ((mh->expected_size != 0) && (mh->expected_size != size))
+ {
+#if GNUNET8_NETWORK_IS_DEAD
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ "Expected %u bytes for message of type %u, got %u\n",
+ mh->expected_size, mh->type, size);
+ GNUNET_break_op (0);
+#endif
+ return GNUNET_SYSERR;
+ }
+ if (sender != NULL)
+ {
+ if (0 == sender->suspended)
+ {
+ sender->warn_start = GNUNET_TIME_absolute_get ();
+ sender->warn_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
+ &warn_no_receive_done, sender);
+ sender->warn_type = type;
+ }
+ sender->suspended++;
+ }
+ mh->callback (mh->callback_cls, sender, message);
+ found = GNUNET_YES;
+ }
+ i++;
+ }
+ pos = pos->next;
+ }
+ if (found == GNUNET_NO)
+ {
+ 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 are receiving an incoming message. Process it.
+ *
+ * @param cls our closure (handle for the client)
+ * @param buf buffer with data received from network
+ * @param available number of bytes available in buf
+ * @param addr address of the sender
+ * @param addrlen length of addr
+ * @param errCode code indicating errors receiving, 0 for success
+ */
+static void
+process_incoming (void *cls, const void *buf, size_t available,
+ const struct sockaddr *addr, socklen_t addrlen, int errCode);
+
+
+/**
+ * Process messages from the client's message tokenizer until either
+ * the tokenizer is empty (and then schedule receiving more), or
+ * until some handler is not immediately done (then wait for restart_processing)
+ * or shutdown.
+ *
+ * @param client the client to process, RC must have already been increased
+ * using GNUNET_SERVER_client_keep and will be decreased by one in this
+ * function
+ * @param ret GNUNET_NO to start processing from the buffer,
+ * GNUNET_OK if the mst buffer is drained and we should instantly go back to receiving
+ * GNUNET_SYSERR if we should instantly abort due to error in a previous step
+ */
+static void
+process_mst (struct GNUNET_SERVER_Client *client, int ret)
+{
+ while ((ret != GNUNET_SYSERR) && (client->server != NULL) &&
+ (GNUNET_YES != client->shutdown_now) && (0 == client->suspended))
+ {
+ if (ret == GNUNET_OK)
+ {
+ client->receive_pending = GNUNET_YES;
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server re-enters receive loop, timeout: %llu.\n",
+ client->idle_timeout.rel_value);
+#endif
+ GNUNET_CONNECTION_receive (client->connection,
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ client->idle_timeout, &process_incoming,
+ client);
+ break;
+ }
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server processes additional messages instantly.\n");
+#endif
+ if (client->server->mst_receive != NULL)
+ ret =
+ client->server->mst_receive (client->server->mst_cls, client->mst,
+ client, NULL, 0, GNUNET_NO, GNUNET_YES);
+ else
+ ret =
+ GNUNET_SERVER_mst_receive (client->mst, client, NULL, 0, GNUNET_NO,
+ GNUNET_YES);
+ }
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server leaves instant processing loop: ret = %d, server = %p, shutdown = %d, suspended = %u\n",
+ ret, client->server, client->shutdown_now, client->suspended);
+#endif
+
+ if (ret == GNUNET_NO)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server has more data pending but is suspended.\n");
+#endif
+ client->receive_pending = GNUNET_SYSERR; /* data pending */
+ }
+ if ((ret == GNUNET_SYSERR) || (GNUNET_YES == client->shutdown_now))
+ GNUNET_SERVER_client_disconnect (client);
+ GNUNET_SERVER_client_drop (client);
+}
+
+
+/**
+ * We are receiving an incoming message. Process it.
+ *
+ * @param cls our closure (handle for the client)
+ * @param buf buffer with data received from network
+ * @param available number of bytes available in buf
+ * @param addr address of the sender
+ * @param addrlen length of addr
+ * @param errCode code indicating errors receiving, 0 for success
+ */
+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;
+ struct GNUNET_TIME_Absolute end;
+ struct GNUNET_TIME_Absolute now;
+ int ret;
+
+ GNUNET_assert (client->receive_pending == GNUNET_YES);
+ client->receive_pending = GNUNET_NO;
+ now = GNUNET_TIME_absolute_get ();
+ end = GNUNET_TIME_absolute_add (client->last_activity, client->idle_timeout);
+
+ if ((buf == NULL) && (available == 0) && (addr == NULL) && (errCode == 0) &&
+ (client->shutdown_now != GNUNET_YES) && (server != NULL) &&
+ (GNUNET_YES == GNUNET_CONNECTION_check (client->connection)) &&
+ (end.abs_value > now.abs_value))
+ {
+ /* wait longer, timeout changed (i.e. due to us sending) */
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Receive time out, but no disconnect due to sending (%p)\n",
+ GNUNET_a2s (addr, addrlen));
+#endif
+ client->receive_pending = GNUNET_YES;
+ GNUNET_CONNECTION_receive (client->connection,
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ GNUNET_TIME_absolute_get_remaining (end),
+ &process_incoming, client);
+ return;
+ }
+ if ((buf == NULL) || (available == 0) || (errCode != 0) || (server == NULL) ||
+ (client->shutdown_now == GNUNET_YES) ||
+ (GNUNET_YES != GNUNET_CONNECTION_check (client->connection)))
+ {
+ /* other side closed connection, error connecting, etc. */
+ GNUNET_SERVER_client_disconnect (client);
+ return;
+ }
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Server receives %u bytes from `%s'.\n",
+ (unsigned int) available, GNUNET_a2s (addr, addrlen));
+#endif
+ GNUNET_SERVER_client_keep (client);
+ client->last_activity = now;
+
+ if (server->mst_receive != NULL)
+ ret =
+ client->server->mst_receive (client->server->mst_cls, client->mst,
+ client, buf, available, GNUNET_NO, GNUNET_YES);
+ else
+ ret =
+ GNUNET_SERVER_mst_receive (client->mst, client, buf, available, GNUNET_NO,
+ GNUNET_YES);
+
+ process_mst (client, ret);
+}
+
+
+/**
+ * Task run to start again receiving from the network
+ * and process requests.
+ *
+ * @param cls our 'struct GNUNET_SERVER_Client*' to process more requests from
+ * @param tc scheduler context (unused)
+ */
+static void
+restart_processing (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ struct GNUNET_SERVER_Handle *server = client->server;
+
+ client->restart_task = GNUNET_SCHEDULER_NO_TASK;
+ if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) &&
+ (GNUNET_NO == server->clients_ignore_shutdown))
+ {
+ GNUNET_SERVER_client_disconnect (client);
+ return;
+ }
+ if (client->receive_pending == GNUNET_NO)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Server begins to read again from client.\n");
+#endif
+ client->receive_pending = GNUNET_YES;
+ GNUNET_CONNECTION_receive (client->connection,
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ client->idle_timeout, &process_incoming, client);
+ return;
+ }
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server continues processing messages still in the buffer.\n");
+#endif
+ GNUNET_SERVER_client_keep (client);
+ client->receive_pending = GNUNET_NO;
+ process_mst (client, GNUNET_NO);
+}
+
+
+/**
+ * This function is called whenever our inbound message tokenizer has
+ * received a complete message.
+ *
+ * @param cls closure (struct GNUNET_SERVER_Handle)
+ * @param client identification of the client (struct GNUNET_SERVER_Client*)
+ * @param message the actual message
+ */
+static void
+client_message_tokenizer_callback (void *cls, void *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct GNUNET_SERVER_Handle *server = cls;
+ struct GNUNET_SERVER_Client *sender = client;
+ int ret;
+
+#if DEBUG_SERVER
+
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Tokenizer gives server message of type %u from client\n",
+ ntohs (message->type));
+#endif
+ sender->in_process_client_buffer = GNUNET_YES;
+ ret = GNUNET_SERVER_inject (server, sender, message);
+ sender->in_process_client_buffer = GNUNET_NO;
+ if (GNUNET_OK != ret)
+ GNUNET_SERVER_client_disconnect (sender);
+}
+
+
+/**
+ * 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_CONNECTION_Handle *connection)
+{
+ struct GNUNET_SERVER_Client *client;
+
+ client = GNUNET_malloc (sizeof (struct GNUNET_SERVER_Client));
+ client->connection = connection;
+ client->mst =
+ GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, server);
+ client->reference_count = 1;
+ client->server = server;
+ client->last_activity = GNUNET_TIME_absolute_get ();
+ client->next = server->clients;
+ client->idle_timeout = server->idle_timeout;
+ server->clients = client;
+ client->receive_pending = GNUNET_YES;
+ client->callback = NULL;
+ client->callback_cls = NULL;
+
+ if (server->mst_create != NULL)
+ client->mst =
+ server->mst_create (server->mst_cls, client);
+ else
+ client->mst =
+ GNUNET_SERVER_mst_create (&client_message_tokenizer_callback, server);
+
+ GNUNET_CONNECTION_receive (client->connection,
+ GNUNET_SERVER_MAX_MESSAGE_SIZE - 1,
+ client->idle_timeout, &process_incoming, client);
+ return client;
+}
+
+
+/**
+ * Change the timeout for a particular client. Decreasing the timeout
+ * may not go into effect immediately (only after the previous timeout
+ * times out or activity happens on the socket).
+ *
+ * @param client the client to update
+ * @param timeout new timeout for activities on the socket
+ */
+void
+GNUNET_SERVER_client_set_timeout (struct GNUNET_SERVER_Client *client,
+ struct GNUNET_TIME_Relative timeout)
+{
+ client->idle_timeout = timeout;
+}
+
+
+void
+GNUNET_SERVER_client_set_finish_pending_write (struct GNUNET_SERVER_Client *client,
+ int finish)
+{
+ client->finish_pending_write = finish;
+}
+
+
+/**
+ * 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->shutdown_now == GNUNET_YES) && (client->reference_count == 0))
+ GNUNET_SERVER_client_disconnect (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)
+{
+ return GNUNET_CONNECTION_get_address (client->connection, 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 stop notifying us whenever a client disconnects.
+ *
+ * @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_cancel (struct GNUNET_SERVER_Handle *server,
+ GNUNET_SERVER_DisconnectCallback
+ callback, void *callback_cls)
+{
+ struct NotifyList *pos;
+ struct NotifyList *prev;
+
+ prev = NULL;
+ pos = server->disconnect_notify_list;
+ while (pos != NULL)
+ {
+ if ((pos->callback == callback) && (pos->callback_cls == callback_cls))
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (pos == NULL)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (prev == NULL)
+ server->disconnect_notify_list = pos->next;
+ else
+ prev->next = pos->next;
+ GNUNET_free (pos);
+}
+
+
+/**
+ * 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)
+{
+ struct GNUNET_SERVER_Client *prev;
+ struct GNUNET_SERVER_Client *pos;
+ struct GNUNET_SERVER_Handle *server;
+ struct NotifyList *n;
+ unsigned int rc;
+
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Client is being disconnected from the server.\n");
+#endif
+ if (client->restart_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (client->restart_task);
+ client->restart_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (client->warn_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (client->warn_task);
+ client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (GNUNET_YES == client->receive_pending)
+ {
+ GNUNET_CONNECTION_receive_cancel (client->connection);
+ client->receive_pending = GNUNET_NO;
+ }
+
+ rc = client->reference_count;
+ if (client->shutdown_now != GNUNET_YES)
+ {
+ server = client->server;
+ client->shutdown_now = GNUNET_YES;
+ 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;
+ if (client->restart_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (client->restart_task);
+ client->restart_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (client->warn_task != GNUNET_SCHEDULER_NO_TASK)
+ {
+ GNUNET_SCHEDULER_cancel (client->warn_task);
+ client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ n = server->disconnect_notify_list;
+ while (n != NULL)
+ {
+ n->callback (n->callback_cls, client);
+ n = n->next;
+ }
+ }
+ if (rc > 0)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "RC still positive, not destroying everything.\n");
+#endif
+ return;
+ }
+ if (client->in_process_client_buffer == GNUNET_YES)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Still processing inputs, not destroying everything.\n");
+#endif
+ return;
+ }
+
+ if (client->persist == GNUNET_YES)
+ GNUNET_CONNECTION_persist_ (client->connection);
+ GNUNET_CONNECTION_destroy (client->connection, client->finish_pending_write);
+
+ if (client->server->mst_destroy != NULL)
+ client->server->mst_destroy (client->server->mst_cls, client->mst);
+ else
+ GNUNET_SERVER_mst_destroy (client->mst);
+
+ GNUNET_free (client);
+}
+
+
+/**
+ * Disable the "CORK" feature for communication with the given client,
+ * forcing the OS to immediately flush the buffer on transmission
+ * instead of potentially buffering multiple messages.
+ *
+ * @param client handle to the client
+ * @return GNUNET_OK on success
+ */
+int
+GNUNET_SERVER_client_disable_corking (struct GNUNET_SERVER_Client *client)
+{
+ return GNUNET_CONNECTION_disable_corking (client->connection);
+}
+
+
+/**
+ * Wrapper for transmission notification that calls the original
+ * callback and update the last activity time for our connection.
+ *
+ * @param cls the 'struct GNUNET_SERVER_Client'
+ * @param size number of bytes we can transmit
+ * @param buf where to copy the message
+ * @return number of bytes actually transmitted
+ */
+static size_t
+transmit_ready_callback_wrapper (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_SERVER_Client *client = cls;
+ size_t ret;
+
+ ret = client->callback (client->callback_cls, size, buf);
+ if (ret > 0)
+ client->last_activity = GNUNET_TIME_absolute_get ();
+ return ret;
+}
+
+
+/**
+ * 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_CONNECTION_notify_transmit_ready_cancel.
+ * NULL if we are already going to notify someone else (busy)
+ */
+struct GNUNET_CONNECTION_TransmitHandle *
+GNUNET_SERVER_notify_transmit_ready (struct GNUNET_SERVER_Client *client,
+ size_t size,
+ struct GNUNET_TIME_Relative timeout,
+ GNUNET_CONNECTION_TransmitReadyNotify
+ callback, void *callback_cls)
+{
+ client->callback_cls = callback_cls;
+ client->callback = callback;
+ return GNUNET_CONNECTION_notify_transmit_ready (client->connection, size,
+ timeout,
+ &transmit_ready_callback_wrapper,
+ client);
+}
+
+
+/**
+ * Set the persistent flag on this client, used to setup client connection
+ * to only be killed when the service it's connected to is actually dead.
+ *
+ * @param client the client to set the persistent flag on
+ */
+void
+GNUNET_SERVER_client_persist_ (struct GNUNET_SERVER_Client *client)
+{
+ client->persist = GNUNET_YES;
+}
+
+
+/**
+ * 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_NO to close the connection (normal behavior)
+ * GNUNET_SYSERR to close the connection (signal
+ * serious error)
+ */
+void
+GNUNET_SERVER_receive_done (struct GNUNET_SERVER_Client *client, int success)
+{
+ if (client == NULL)
+ return;
+ GNUNET_assert (client->suspended > 0);
+ client->suspended--;
+ if (success != GNUNET_OK)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GNUNET_SERVER_receive_done called with failure indication\n");
+#endif
+ GNUNET_SERVER_client_disconnect (client);
+ return;
+ }
+ if (client->suspended > 0)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GNUNET_SERVER_receive_done called, but more clients pending\n");
+#endif
+ return;
+ }
+ if (GNUNET_SCHEDULER_NO_TASK != client->warn_task)
+ {
+ GNUNET_SCHEDULER_cancel (client->warn_task);
+ client->warn_task = GNUNET_SCHEDULER_NO_TASK;
+ }
+ if (client->in_process_client_buffer == GNUNET_YES)
+ {
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GNUNET_SERVER_receive_done called while still in processing loop\n");
+#endif
+ return;
+ }
+ if ((client->server == NULL) || (GNUNET_YES == client->shutdown_now))
+ {
+ GNUNET_SERVER_client_disconnect (client);
+ return;
+ }
+#if DEBUG_SERVER
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "GNUNET_SERVER_receive_done causes restart in reading from the socket\n");
+#endif
+ GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == client->restart_task);
+ client->restart_task = GNUNET_SCHEDULER_add_now (&restart_processing, client);
+}
+
+
+/**
+ * Configure this server's connections to continue handling client
+ * requests as usual even after we get a shutdown signal. The change
+ * only applies to clients that connect to the server from the outside
+ * using TCP after this call. Clients managed previously or those
+ * added using GNUNET_SERVER_connect_socket and
+ * GNUNET_SERVER_connect_callback are not affected by this option.
+ *
+ * @param h server handle
+ * @param do_ignore GNUNET_YES to ignore, GNUNET_NO to restore default
+ */
+void
+GNUNET_SERVER_ignore_shutdown (struct GNUNET_SERVER_Handle *h, int do_ignore)
+{
+ h->clients_ignore_shutdown = do_ignore;
+}
+
+/* end of server.c */
diff --git a/src/util/server_mst.c b/src/util/server_mst.c
new file mode 100644
index 0000000..6161770
--- /dev/null
+++ b/src/util/server_mst.c
@@ -0,0 +1,321 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010 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_mst.c
+ * @brief convenience functions for handling inbound message buffers
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_time_lib.h"
+
+#define DEBUG_SERVER_MST GNUNET_EXTRA_LOGGING
+
+#if HAVE_UNALIGNED_64_ACCESS
+#define ALIGN_FACTOR 4
+#else
+#define ALIGN_FACTOR 8
+#endif
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+
+/**
+ * Handle to a message stream tokenizer.
+ */
+struct GNUNET_SERVER_MessageStreamTokenizer
+{
+
+ /**
+ * Function to call on completed messages.
+ */
+ GNUNET_SERVER_MessageTokenizerCallback cb;
+
+ /**
+ * Closure for cb.
+ */
+ void *cb_cls;
+
+ /**
+ * Size of the buffer (starting at 'hdr').
+ */
+ size_t curr_buf;
+
+ /**
+ * How many bytes in buffer have we already processed?
+ */
+ size_t off;
+
+ /**
+ * How many bytes in buffer are valid right now?
+ */
+ size_t pos;
+
+ /**
+ * Beginning of the buffer. Typed like this to force alignment.
+ */
+ struct GNUNET_MessageHeader *hdr;
+
+};
+
+
+
+/**
+ * Create a message stream tokenizer.
+ *
+ * @param cb function to call on completed messages
+ * @param cb_cls closure for cb
+ * @return handle to tokenizer
+ */
+struct GNUNET_SERVER_MessageStreamTokenizer *
+GNUNET_SERVER_mst_create (GNUNET_SERVER_MessageTokenizerCallback cb,
+ void *cb_cls)
+{
+ struct GNUNET_SERVER_MessageStreamTokenizer *ret;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_MessageStreamTokenizer));
+ ret->hdr = GNUNET_malloc (GNUNET_SERVER_MIN_BUFFER_SIZE);
+ ret->curr_buf = GNUNET_SERVER_MIN_BUFFER_SIZE;
+ ret->cb = cb;
+ ret->cb_cls = cb_cls;
+ return ret;
+}
+
+
+/**
+ * Add incoming data to the receive buffer and call the
+ * callback for all complete messages.
+ *
+ * @param mst tokenizer to use
+ * @param client_identity ID of client for which this is a buffer
+ * @param buf input data to add
+ * @param size number of bytes in buf
+ * @param purge should any excess bytes in the buffer be discarded
+ * (i.e. for packet-based services like UDP)
+ * @param one_shot only call callback once, keep rest of message in buffer
+ * @return GNUNET_OK if we are done processing (need more data)
+ * GNUNET_NO if one_shot was set and we have another message ready
+ * GNUNET_SYSERR if the data stream is corrupt
+ */
+int
+GNUNET_SERVER_mst_receive (struct GNUNET_SERVER_MessageStreamTokenizer *mst,
+ void *client_identity, const char *buf, size_t size,
+ int purge, int one_shot)
+{
+ const struct GNUNET_MessageHeader *hdr;
+ size_t delta;
+ uint16_t want;
+ char *ibuf;
+ int need_align;
+ unsigned long offset;
+ int ret;
+
+ GNUNET_assert (mst->off <= mst->pos);
+ GNUNET_assert (mst->pos <= mst->curr_buf);
+#if DEBUG_SERVER_MST
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server-mst receives %u bytes with %u bytes already in private buffer\n",
+ (unsigned int) size, (unsigned int) (mst->pos - mst->off));
+#endif
+ ret = GNUNET_OK;
+ ibuf = (char *) mst->hdr;
+ while (mst->pos > 0)
+ {
+do_align:
+ GNUNET_assert (mst->pos >= mst->off);
+ if ((mst->curr_buf - mst->off < sizeof (struct GNUNET_MessageHeader)) ||
+ (0 != (mst->off % ALIGN_FACTOR)))
+ {
+ /* need to align or need more space */
+ mst->pos -= mst->off;
+ memmove (ibuf, &ibuf[mst->off], mst->pos);
+ mst->off = 0;
+ }
+ if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
+ {
+ delta =
+ GNUNET_MIN (sizeof (struct GNUNET_MessageHeader) -
+ (mst->pos - mst->off), size);
+ memcpy (&ibuf[mst->pos], buf, delta);
+ mst->pos += delta;
+ buf += delta;
+ size -= delta;
+ }
+ if (mst->pos - mst->off < sizeof (struct GNUNET_MessageHeader))
+ {
+ if (purge)
+ {
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ return GNUNET_OK;
+ }
+ hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
+ want = ntohs (hdr->size);
+ if (want < sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ if ( (mst->curr_buf - mst->off < want) &&
+ (mst->off > 0) )
+ {
+ /* can get more space by moving */
+ mst->pos -= mst->off;
+ memmove (ibuf, &ibuf[mst->off], mst->pos);
+ mst->off = 0;
+ }
+ if (mst->curr_buf < want)
+ {
+ /* need to get more space by growing buffer */
+ GNUNET_assert (0 == mst->off);
+ mst->hdr = GNUNET_realloc (mst->hdr, want);
+ ibuf = (char *) mst->hdr;
+ mst->curr_buf = want;
+ }
+ hdr = (const struct GNUNET_MessageHeader *) &ibuf[mst->off];
+ if (mst->pos - mst->off < want)
+ {
+ delta = GNUNET_MIN (want - (mst->pos - mst->off), size);
+ GNUNET_assert (mst->pos + delta <= mst->curr_buf);
+ memcpy (&ibuf[mst->pos], buf, delta);
+ mst->pos += delta;
+ buf += delta;
+ size -= delta;
+ }
+ if (mst->pos - mst->off < want)
+ {
+ if (purge)
+ {
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ return GNUNET_OK;
+ }
+ if (one_shot == GNUNET_SYSERR)
+ {
+ /* cannot call callback again, but return value saying that
+ * we have another full message in the buffer */
+ ret = GNUNET_NO;
+ goto copy;
+ }
+ if (one_shot == GNUNET_YES)
+ one_shot = GNUNET_SYSERR;
+ mst->off += want;
+ mst->cb (mst->cb_cls, client_identity, hdr);
+ if (mst->off == mst->pos)
+ {
+ /* reset to beginning of buffer, it's free right now! */
+ mst->off = 0;
+ mst->pos = 0;
+ }
+ }
+ GNUNET_assert (0 == mst->pos);
+ while (size > 0)
+ {
+#if DEBUG_SERVER_MST
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server-mst has %u bytes left in inbound buffer\n",
+ (unsigned int) size);
+#endif
+ if (size < sizeof (struct GNUNET_MessageHeader))
+ break;
+ offset = (unsigned long) buf;
+ need_align = (0 != (offset % ALIGN_FACTOR)) ? GNUNET_YES : GNUNET_NO;
+ if (GNUNET_NO == need_align)
+ {
+ /* can try to do zero-copy and process directly from original buffer */
+ hdr = (const struct GNUNET_MessageHeader *) buf;
+ want = ntohs (hdr->size);
+ if (want < sizeof (struct GNUNET_MessageHeader))
+ {
+ GNUNET_break_op (0);
+ mst->off = 0;
+ return GNUNET_SYSERR;
+ }
+ if (size < want)
+ break; /* or not: buffer incomplete, so copy to private buffer... */
+ if (one_shot == GNUNET_SYSERR)
+ {
+ /* cannot call callback again, but return value saying that
+ * we have another full message in the buffer */
+ ret = GNUNET_NO;
+ goto copy;
+ }
+ if (one_shot == GNUNET_YES)
+ one_shot = GNUNET_SYSERR;
+ mst->cb (mst->cb_cls, client_identity, hdr);
+ buf += want;
+ size -= want;
+ }
+ else
+ {
+ /* need to copy to private buffer to align;
+ * yes, we go a bit more spagetti than usual here */
+ goto do_align;
+ }
+ }
+copy:
+ if ((size > 0) && (!purge))
+ {
+ if (size + mst->pos > mst->curr_buf)
+ {
+ mst->hdr = GNUNET_realloc (mst->hdr, size + mst->pos);
+ ibuf = (char *) mst->hdr;
+ mst->curr_buf = size + mst->pos;
+ }
+ GNUNET_assert (size + mst->pos <= mst->curr_buf);
+ memcpy (&ibuf[mst->pos], buf, size);
+ mst->pos += size;
+ }
+ if (purge)
+ {
+ mst->off = 0;
+ mst->pos = 0;
+ }
+#if DEBUG_SERVER_MST
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Server-mst leaves %u bytes in private buffer\n",
+ (unsigned int) (mst->pos - mst->off));
+#endif
+ return ret;
+}
+
+
+/**
+ * Destroys a tokenizer.
+ *
+ * @param mst tokenizer to destroy
+ */
+void
+GNUNET_SERVER_mst_destroy (struct GNUNET_SERVER_MessageStreamTokenizer *mst)
+{
+ GNUNET_free (mst->hdr);
+ GNUNET_free (mst);
+}
+
+
+
+/* end of server_mst.c */
diff --git a/src/util/server_nc.c b/src/util/server_nc.c
new file mode 100644
index 0000000..08ffd4b
--- /dev/null
+++ b/src/util/server_nc.c
@@ -0,0 +1,450 @@
+/*
+ This file is part of GNUnet.
+ (C) 2010 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_nc.c
+ * @brief convenience functions for transmission of
+ * a notification stream
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_container_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_time_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+
+#define DEBUG_SERVER_NC GNUNET_EXTRA_LOGGING
+
+/**
+ * Entry in list of messages pending to be transmitted.
+ */
+struct PendingMessageList
+{
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct PendingMessageList *next;
+
+ /**
+ * This is a doubly-linked list.
+ */
+ struct PendingMessageList *prev;
+
+ /**
+ * Message to transmit (allocated at the end of this
+ * struct, do not free)
+ */
+ const struct GNUNET_MessageHeader *msg;
+
+ /**
+ * Can this message be dropped?
+ */
+ int can_drop;
+
+};
+
+
+/**
+ * Lists of clients we manage for notifications.
+ */
+struct ClientList
+{
+
+ /**
+ * This is a linked list.
+ */
+ struct ClientList *next;
+
+ /**
+ * Overall context this client belongs to.
+ */
+ struct GNUNET_SERVER_NotificationContext *nc;
+
+ /**
+ * Handle to the client.
+ */
+ struct GNUNET_SERVER_Client *client;
+
+ /**
+ * Handle for pending transmission request to the client (or NULL).
+ */
+ struct GNUNET_CONNECTION_TransmitHandle *th;
+
+ /**
+ * Head of linked list of requests queued for transmission.
+ */
+ struct PendingMessageList *pending_head;
+
+ /**
+ * Tail of linked list of requests queued for transmission.
+ */
+ struct PendingMessageList *pending_tail;
+
+ /**
+ * Number of messages currently in the list.
+ */
+ unsigned int num_pending;
+
+};
+
+
+/**
+ * The notification context is the key datastructure for a convenience
+ * API used for transmission of notifications to the client until the
+ * client disconnects (or the notification context is destroyed, in
+ * which case we disconnect these clients). Essentially, all
+ * (notification) messages are queued up until the client is able to
+ * read them.
+ */
+struct GNUNET_SERVER_NotificationContext
+{
+
+ /**
+ * Server we do notifications for.
+ */
+ struct GNUNET_SERVER_Handle *server;
+
+ /**
+ * List of clients receiving notifications.
+ */
+ struct ClientList *clients;
+
+ /**
+ * Maximum number of optional messages to queue per client.
+ */
+ unsigned int queue_length;
+
+};
+
+
+/**
+ * Client has disconnected, clean up.
+ *
+ * @param cls our 'struct GNUNET_SERVER_NotificationContext *'
+ * @param client handle of client that disconnected
+ */
+static void
+handle_client_disconnect (void *cls, struct GNUNET_SERVER_Client *client)
+{
+ struct GNUNET_SERVER_NotificationContext *nc = cls;
+ struct ClientList *pos;
+ struct ClientList *prev;
+ struct PendingMessageList *pml;
+
+ if (client == NULL)
+ {
+ nc->server = NULL;
+ return;
+ }
+ prev = NULL;
+ pos = nc->clients;
+ while (NULL != pos)
+ {
+ if (pos->client == client)
+ break;
+ prev = pos;
+ pos = pos->next;
+ }
+ if (pos == NULL)
+ return;
+#if DEBUG_SERVER_NC
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Client disconnected, cleaning up %u messages in NC queue\n",
+ pos->num_pending);
+#endif
+ if (prev == NULL)
+ nc->clients = pos->next;
+ else
+ prev->next = pos->next;
+ while (NULL != (pml = pos->pending_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (pos->pending_head, pos->pending_tail, pml);
+ GNUNET_free (pml);
+ }
+ if (pos->th != NULL)
+ {
+ GNUNET_CONNECTION_notify_transmit_ready_cancel (pos->th);
+ pos->th = NULL;
+ }
+ GNUNET_SERVER_client_drop (client);
+ GNUNET_free (pos);
+}
+
+
+/**
+ * Create a new notification context.
+ *
+ * @param server server for which this function creates the context
+ * @param queue_length maximum number of messages to keep in
+ * the notification queue; optional messages are dropped
+ * it the queue gets longer than this number of messages
+ * @return handle to the notification context
+ */
+struct GNUNET_SERVER_NotificationContext *
+GNUNET_SERVER_notification_context_create (struct GNUNET_SERVER_Handle *server,
+ unsigned int queue_length)
+{
+ struct GNUNET_SERVER_NotificationContext *ret;
+
+ ret = GNUNET_malloc (sizeof (struct GNUNET_SERVER_NotificationContext));
+ ret->server = server;
+ ret->queue_length = queue_length;
+ GNUNET_SERVER_disconnect_notify (server, &handle_client_disconnect, ret);
+ return ret;
+}
+
+
+/**
+ * Destroy the context, force disconnect for all clients.
+ *
+ * @param nc context to destroy.
+ */
+void
+GNUNET_SERVER_notification_context_destroy (struct
+ GNUNET_SERVER_NotificationContext
+ *nc)
+{
+ struct ClientList *pos;
+ struct PendingMessageList *pml;
+
+ while (NULL != (pos = nc->clients))
+ {
+ nc->clients = pos->next;
+ GNUNET_SERVER_client_drop (pos->client);
+ while (NULL != (pml = pos->pending_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (pos->pending_head, pos->pending_tail, pml);
+ GNUNET_free (pml);
+ }
+ GNUNET_free (pos);
+ }
+ if (nc->server != NULL)
+ GNUNET_SERVER_disconnect_notify_cancel (nc->server,
+ &handle_client_disconnect, nc);
+ GNUNET_free (nc);
+}
+
+
+/**
+ * Add a client to the notification context.
+ *
+ * @param nc context to modify
+ * @param client client to add
+ */
+void
+GNUNET_SERVER_notification_context_add (struct GNUNET_SERVER_NotificationContext
+ *nc,
+ struct GNUNET_SERVER_Client *client)
+{
+ struct ClientList *cl;
+
+ for (cl = nc->clients; NULL != cl; cl = cl->next)
+ if (cl->client == client)
+ return; /* already present */
+ cl = GNUNET_malloc (sizeof (struct ClientList));
+ cl->next = nc->clients;
+ cl->nc = nc;
+ cl->client = client;
+ GNUNET_SERVER_client_keep (client);
+ nc->clients = cl;
+}
+
+
+/**
+ * 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 the 'struct ClientList *'
+ * @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_message (void *cls, size_t size, void *buf)
+{
+ struct ClientList *cl = cls;
+ char *cbuf = buf;
+ struct PendingMessageList *pml;
+ uint16_t msize;
+ size_t ret;
+
+ cl->th = NULL;
+ if (buf == NULL)
+ {
+ /* 'cl' should be freed via disconnect notification shortly */
+#if DEBUG_SERVER_NC
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Failed to transmit message from NC queue to client\n");
+#endif
+ return 0;
+ }
+ ret = 0;
+ while (NULL != (pml = cl->pending_head))
+ {
+ msize = ntohs (pml->msg->size);
+ if (size < msize)
+ break;
+ GNUNET_CONTAINER_DLL_remove (cl->pending_head, cl->pending_tail, pml);
+#if DEBUG_SERVER_NC
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Copying message of type %u and size %u from pending queue to transmission buffer\n",
+ ntohs (pml->msg->type), msize);
+#endif
+ memcpy (&cbuf[ret], pml->msg, msize);
+ ret += msize;
+ size -= msize;
+ GNUNET_free (pml);
+ cl->num_pending--;
+ }
+ if (pml != NULL)
+ {
+#if DEBUG_SERVER_NC
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Have %u messages left in NC queue, will try transmission again\n",
+ cl->num_pending);
+#endif
+ cl->th =
+ GNUNET_SERVER_notify_transmit_ready (cl->client, ntohs (pml->msg->size),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &transmit_message, cl);
+ }
+ else
+ GNUNET_assert (cl->num_pending == 0);
+ return ret;
+}
+
+
+/**
+ * Send a message to a particular client.
+ *
+ * @param nc context to modify
+ * @param client client to transmit to
+ * @param msg message to send
+ * @param can_drop can this message be dropped due to queue length limitations
+ */
+static void
+do_unicast (struct GNUNET_SERVER_NotificationContext *nc,
+ struct ClientList *client, const struct GNUNET_MessageHeader *msg,
+ int can_drop)
+{
+ struct PendingMessageList *pml;
+ uint16_t size;
+
+ if ((client->num_pending > nc->queue_length) && (GNUNET_YES == can_drop))
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ "Dropping message of type %u and size %u due to full queue (%u entries)\n",
+ ntohs (msg->type), ntohs (msg->size), (unsigned int) nc->queue_length);
+ return; /* drop! */
+ }
+ if (client->num_pending > nc->queue_length)
+ {
+ /* FIXME: consider checking for other messages in the
+ * queue that are 'droppable' */
+ }
+ client->num_pending++;
+ size = ntohs (msg->size);
+ pml = GNUNET_malloc (sizeof (struct PendingMessageList) + size);
+ pml->msg = (const struct GNUNET_MessageHeader *) &pml[1];
+ pml->can_drop = can_drop;
+#if DEBUG_SERVER_NC
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Adding message of type %u and size %u to pending queue (which has %u entries)\n",
+ ntohs (msg->type), ntohs (msg->size), (unsigned int) nc->queue_length);
+#endif
+ memcpy (&pml[1], msg, size);
+ /* append */
+ GNUNET_CONTAINER_DLL_insert_tail (client->pending_head, client->pending_tail,
+ pml);
+ if (client->th == NULL)
+ client->th =
+ GNUNET_SERVER_notify_transmit_ready (client->client,
+ ntohs (client->pending_head->
+ msg->size),
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &transmit_message, client);
+}
+
+
+/**
+ * Send a message to a particular client; must have
+ * already been added to the notification context.
+ *
+ * @param nc context to modify
+ * @param client client to transmit to
+ * @param msg message to send
+ * @param can_drop can this message be dropped due to queue length limitations
+ */
+void
+GNUNET_SERVER_notification_context_unicast (struct
+ GNUNET_SERVER_NotificationContext
+ *nc,
+ struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader
+ *msg, int can_drop)
+{
+ struct ClientList *pos;
+
+ pos = nc->clients;
+ while (NULL != pos)
+ {
+ if (pos->client == client)
+ break;
+ pos = pos->next;
+ }
+ GNUNET_assert (pos != NULL);
+ do_unicast (nc, pos, msg, can_drop);
+}
+
+
+/**
+ * Send a message to all clients of this context.
+ *
+ * @param nc context to modify
+ * @param msg message to send
+ * @param can_drop can this message be dropped due to queue length limitations
+ */
+void
+GNUNET_SERVER_notification_context_broadcast (struct
+ GNUNET_SERVER_NotificationContext
+ *nc,
+ const struct GNUNET_MessageHeader
+ *msg, int can_drop)
+{
+ struct ClientList *pos;
+
+ pos = nc->clients;
+ while (NULL != pos)
+ {
+ do_unicast (nc, pos, msg, can_drop);
+ pos = pos->next;
+ }
+}
+
+
+/* end of server_nc.c */
diff --git a/src/util/server_tc.c b/src/util/server_tc.c
new file mode 100644
index 0000000..ce40db1
--- /dev/null
+++ b/src/util/server_tc.c
@@ -0,0 +1,247 @@
+/*
+ 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_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_time_lib.h"
+
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+
+/**
+ * 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_transmit_context_destroy (tc, GNUNET_SYSERR);
+ 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_SERVER_client_drop (tc->client);
+ 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_transmit_context_destroy (tc, GNUNET_SYSERR);
+ }
+ }
+ 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));
+ GNUNET_SERVER_client_keep (client);
+ 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_data (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);
+}
+
+
+/**
+ * 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 msg message to append
+ */
+void
+GNUNET_SERVER_transmit_context_append_message (struct
+ GNUNET_SERVER_TransmitContext
+ *tc,
+ const struct GNUNET_MessageHeader
+ *msg)
+{
+ struct GNUNET_MessageHeader *m;
+ uint16_t size;
+
+ size = ntohs (msg->size);
+ tc->buf = GNUNET_realloc (tc->buf, tc->total + size);
+ m = (struct GNUNET_MessageHeader *) &tc->buf[tc->total];
+ tc->total += size;
+ memcpy (m, msg, size);
+}
+
+
+/**
+ * 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_transmit_context_destroy (tc, GNUNET_SYSERR);
+ }
+}
+
+
+/**
+ * Destroy a transmission context. This function must not be called
+ * after 'GNUNET_SERVER_transmit_context_run'.
+ *
+ * @param tc transmission context to destroy
+ * @param success code to give to 'GNUNET_SERVER_receive_done' for
+ * the client: GNUNET_OK to keep the connection open and
+ * continue to receive
+ * GNUNET_NO to close the connection (normal behavior)
+ * GNUNET_SYSERR to close the connection (signal
+ * serious error)
+ */
+void
+GNUNET_SERVER_transmit_context_destroy (struct GNUNET_SERVER_TransmitContext
+ *tc, int success)
+{
+ GNUNET_SERVER_receive_done (tc->client, success);
+ GNUNET_SERVER_client_drop (tc->client);
+ 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 0000000..243e7da
--- /dev/null
+++ b/src/util/service.c
@@ -0,0 +1,1840 @@
+/*
+ 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_resolver_service.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_service_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+#define LOG_STRERROR_FILE(kind,syscall,filename) GNUNET_log_from_strerror_file (kind, "util", syscall, filename)
+
+#define DEBUG_SERVICE GNUNET_EXTRA_LOGGING
+
+/* ******************* 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
+ * <tt>[network/netmask;]*</tt> (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).
+ * <p>
+ * @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)
+ {
+ 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)
+ {
+ 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
+ {
+ 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)
+ {
+ 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;
+ }
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Invalid format for IP: `%s'\n"),
+ &routeList[pos]);
+ GNUNET_free (result);
+ return NULL; /* error */
+ }
+ if (pos < strlen (routeList))
+ {
+ 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
+ * <tt>[network/netmask;]*</tt> (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.
+ * <p>
+ * @param routeListX 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] != ';')
+ {
+ 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)
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Wrong format `%s' for netmask\n"),
+ &routeList[slash + 1]);
+ else
+ {
+ errno = save;
+ 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)
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Wrong format `%s' for network\n"),
+ &routeList[slash + 1]);
+ else
+ LOG_STRERROR (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 add 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.
+ */
+ const struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ /**
+ * Handle for the server.
+ */
+ struct GNUNET_SERVER_Handle *server;
+
+ /**
+ * NULL-terminated array of addresses to bind to, NULL if we got pre-bound
+ * listen sockets.
+ */
+ struct sockaddr **addrs;
+
+ /**
+ * 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;
+
+ /**
+ * Array of the lengths of the entries in addrs.
+ */
+ socklen_t *addrlens;
+
+ /**
+ * NULL-terminated array of listen sockets we should take over.
+ */
+ struct GNUNET_NETWORK_Handle **lsocks;
+
+ /**
+ * Idle timeout for server.
+ */
+ struct GNUNET_TIME_Relative timeout;
+
+ /**
+ * 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;
+
+ /**
+ * Do we require a matching UID for UNIX domain socket connections?
+ * GNUNET_NO means that the UID does not have to match (however,
+ * "match_gid" may still impose other access control checks).
+ */
+ int match_uid;
+
+ /**
+ * Do we require a matching GID for UNIX domain socket connections?
+ * Ignored if "match_uid" is GNUNET_YES. Note that this is about
+ * checking that the client's UID is in our group OR that the
+ * client's GID is our GID. If both "match_gid" and "match_uid" are
+ * "GNUNET_NO", all users on the local system have access.
+ */
+ int match_gid;
+
+ /**
+ * Our options.
+ */
+ enum GNUNET_SERVICE_Options options;
+
+};
+
+
+/* ****************** 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 client identification of the client
+ * @param message the actual message
+ */
+static void
+handle_test (void *cls, 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);
+}
+
+
+/**
+ * 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)},
+ {NULL, NULL, 0, 0}
+};
+
+
+
+/* ****************** service core routines ************** */
+
+
+/**
+ * Check if access to the service is allowed from the given address.
+ *
+ * @param cls closure
+ * @param uc credentials, if available, otherwise NULL
+ * @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).
+ */
+static int
+check_access (void *cls, const struct GNUNET_CONNECTION_Credentials *uc,
+ 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;
+
+ switch (addr->sa_family)
+ {
+ case AF_INET:
+ GNUNET_assert (addrlen == sizeof (struct sockaddr_in));
+ i4 = (const struct sockaddr_in *) addr;
+ 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)));
+ break;
+ case AF_INET6:
+ GNUNET_assert (addrlen == sizeof (struct sockaddr_in6));
+ i6 = (const struct sockaddr_in6 *) addr;
+ 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)));
+ break;
+#ifndef WINDOWS
+ case AF_UNIX:
+ ret = GNUNET_OK; /* always OK for now */
+ if (sctx->match_uid == GNUNET_YES)
+ {
+ /* UID match required */
+ ret = (uc != NULL) && (uc->uid == geteuid ());
+ }
+ else if (sctx->match_gid == GNUNET_YES)
+ {
+ /* group match required */
+ if (uc == NULL)
+ {
+ /* no credentials, group match not possible */
+ ret = GNUNET_NO;
+ }
+ else
+ {
+ struct group *grp;
+ unsigned int i;
+
+ if (uc->gid != getegid())
+ {
+ /* default group did not match, but maybe the user is in our group, let's check */
+ grp = getgrgid (getegid ());
+ if (NULL == grp)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "getgrgid");
+ return GNUNET_NO;
+ }
+ ret = GNUNET_NO;
+ for (i=0; NULL != grp->gr_mem[i]; i++)
+ {
+ struct passwd *nam = getpwnam (grp->gr_mem[i]);
+ if (NULL == nam)
+ continue; /* name in group that is not in user DB !? */
+ if (nam->pw_uid == uc->uid)
+ {
+ /* yes, uid is in our group, allow! */
+ ret = GNUNET_YES;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (GNUNET_NO == ret)
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("Access denied to UID %d / GID %d\n"),
+ (uc == NULL) ? -1 : uc->uid, (uc == NULL) ? -1 : uc->gid);
+ break;
+#endif
+ default:
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("Unknown address family %d\n"),
+ addr->sa_family);
+ return GNUNET_SYSERR;
+ }
+ if (ret != GNUNET_OK)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("Access from `%s' denied to service `%s'\n"), GNUNET_a2s (addr,
+ addrlen),
+ 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)))
+ {
+ 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)))
+ {
+ 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;
+}
+
+/**
+ * Add the given UNIX domain path as an address to the
+ * list (as the first entry).
+ *
+ * @param saddrs array to update
+ * @param saddrlens where to store the address length
+ * @param unixpath path to add
+ */
+static void
+add_unixpath (struct sockaddr **saddrs, socklen_t * saddrlens,
+ const char *unixpath)
+{
+#ifdef AF_UNIX
+ struct sockaddr_un *un;
+ size_t slen;
+
+ un = GNUNET_malloc (sizeof (struct sockaddr_un));
+ un->sun_family = AF_UNIX;
+ slen = strlen (unixpath) + 1;
+ if (slen >= sizeof (un->sun_path))
+ slen = sizeof (un->sun_path) - 1;
+ memcpy (un->sun_path, unixpath, slen);
+ un->sun_path[slen] = '\0';
+ slen = sizeof (struct sockaddr_un);
+#if LINUX
+ un->sun_path[0] = '\0';
+#endif
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ un->sun_len = (u_char) slen;
+#endif
+ *saddrs = (struct sockaddr *) un;
+ *saddrlens = slen;
+#else
+ /* this function should never be called
+ * unless AF_UNIX is defined! */
+ GNUNET_assert (0);
+#endif
+}
+
+
+/**
+ * Get the list of addresses that a server for the given service
+ * should bind to.
+ *
+ * @param serviceName name of the service
+ * @param cfg configuration (which specifies the addresses)
+ * @param addrs set (call by reference) to an array of pointers to the
+ * addresses the server should bind to and listen on; the
+ * array will be NULL-terminated (on success)
+ * @param addr_lens set (call by reference) to an array of the lengths
+ * of the respective 'struct sockaddr' struct in the 'addrs'
+ * array (on success)
+ * @return number of addresses found on success,
+ * GNUNET_SYSERR if the configuration
+ * did not specify reasonable finding information or
+ * if it specified a hostname that could not be resolved;
+ * GNUNET_NO if the number of addresses configured is
+ * zero (in this case, '*addrs' and '*addr_lens' will be
+ * set to NULL).
+ */
+int
+GNUNET_SERVICE_get_server_addresses (const char *serviceName,
+ const struct GNUNET_CONFIGURATION_Handle
+ *cfg, struct sockaddr ***addrs,
+ socklen_t ** addr_lens)
+{
+ int disablev6;
+ struct GNUNET_NETWORK_Handle *desc;
+ unsigned long long port;
+ char *unixpath;
+ struct addrinfo hints;
+ struct addrinfo *res;
+ struct addrinfo *pos;
+ struct addrinfo *next;
+ unsigned int i;
+ int resi;
+ int ret;
+ struct sockaddr **saddrs;
+ socklen_t *saddrlens;
+ char *hostname;
+
+ *addrs = NULL;
+ *addr_lens = NULL;
+ desc = NULL;
+ if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "DISABLEV6"))
+ {
+ if (GNUNET_SYSERR ==
+ (disablev6 =
+ GNUNET_CONFIGURATION_get_value_yesno (cfg, serviceName, "DISABLEV6")))
+ return GNUNET_SYSERR;
+ }
+ else
+ disablev6 = GNUNET_NO;
+
+ if (!disablev6)
+ {
+ /* probe IPv6 support */
+ desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
+ if (NULL == desc)
+ {
+ if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) ||
+ (errno == EACCES))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
+ return GNUNET_SYSERR;
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _
+ ("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"),
+ serviceName, STRERROR (errno));
+ disablev6 = GNUNET_YES;
+ }
+ else
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
+ desc = NULL;
+ }
+ }
+
+ port = 0;
+ if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "PORT"))
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (cfg, serviceName,
+ "PORT", &port));
+ if (port > 65535)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Require valid port number for service `%s' in configuration!\n"),
+ serviceName);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ if (GNUNET_CONFIGURATION_have_value (cfg, serviceName, "BINDTO"))
+ {
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, serviceName,
+ "BINDTO", &hostname));
+ }
+ else
+ hostname = NULL;
+
+ unixpath = NULL;
+#ifdef AF_UNIX
+ if ((GNUNET_YES ==
+ GNUNET_CONFIGURATION_have_value (cfg, serviceName, "UNIXPATH")) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_string (cfg, serviceName, "UNIXPATH",
+ &unixpath)) &&
+ (0 < strlen (unixpath)))
+ {
+ /* probe UNIX support */
+ struct sockaddr_un s_un;
+
+ if (strlen (unixpath) >= sizeof (s_un.sun_path))
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING,
+ _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath,
+ sizeof (s_un.sun_path));
+ GNUNET_free_non_null (hostname);
+ GNUNET_free (unixpath);
+ return GNUNET_SYSERR;
+ }
+
+ desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0);
+ if (NULL == desc)
+ {
+ if ((errno == ENOBUFS) || (errno == ENOMEM) || (errno == ENFILE) ||
+ (errno == EACCES))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket");
+ GNUNET_free_non_null (hostname);
+ GNUNET_free (unixpath);
+ return GNUNET_SYSERR;
+ }
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _
+ ("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"),
+ serviceName, STRERROR (errno));
+ GNUNET_free (unixpath);
+ unixpath = NULL;
+ }
+ else
+ {
+ GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc));
+ desc = NULL;
+ }
+ }
+#endif
+
+ if ((port == 0) && (unixpath == NULL))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"),
+ serviceName);
+ GNUNET_free_non_null (hostname);
+ return GNUNET_SYSERR;
+ }
+ if (port == 0)
+ {
+ saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc (2 * sizeof (socklen_t));
+ add_unixpath (saddrs, saddrlens, unixpath);
+ GNUNET_free_non_null (unixpath);
+ GNUNET_free_non_null (hostname);
+ *addrs = saddrs;
+ *addr_lens = saddrlens;
+ return 1;
+ }
+
+ if (hostname != NULL)
+ {
+#if DEBUG_SERVICE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Resolving `%s' since that is where `%s' will bind to.\n", hostname,
+ serviceName);
+#endif
+ memset (&hints, 0, sizeof (struct addrinfo));
+ if (disablev6)
+ hints.ai_family = AF_INET;
+ if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) ||
+ (res == NULL))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to resolve `%s': %s\n"), hostname,
+ gai_strerror (ret));
+ GNUNET_free (hostname);
+ GNUNET_free_non_null (unixpath);
+ return GNUNET_SYSERR;
+ }
+ next = res;
+ i = 0;
+ while (NULL != (pos = next))
+ {
+ next = pos->ai_next;
+ if ((disablev6) && (pos->ai_family == AF_INET6))
+ continue;
+ i++;
+ }
+ if (0 == i)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR, _("Failed to find %saddress for `%s'.\n"),
+ disablev6 ? "IPv4 " : "", hostname);
+ freeaddrinfo (res);
+ GNUNET_free (hostname);
+ GNUNET_free_non_null (unixpath);
+ return GNUNET_SYSERR;
+ }
+ resi = i;
+ if (NULL != unixpath)
+ resi++;
+ saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
+ i = 0;
+ if (NULL != unixpath)
+ {
+ add_unixpath (saddrs, saddrlens, unixpath);
+ i++;
+ }
+ next = res;
+ while (NULL != (pos = next))
+ {
+ next = pos->ai_next;
+ if ((disablev6) && (pos->ai_family == AF_INET6))
+ continue;
+ if ((pos->ai_protocol != IPPROTO_TCP) && (pos->ai_protocol != 0))
+ continue; /* not TCP */
+ if ((pos->ai_socktype != SOCK_STREAM) && (pos->ai_socktype != 0))
+ continue; /* huh? */
+#if DEBUG_SERVICE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n",
+ serviceName, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen));
+#endif
+ if (pos->ai_family == AF_INET)
+ {
+ GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in));
+ saddrlens[i] = pos->ai_addrlen;
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+ memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
+ ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
+ }
+ else
+ {
+ GNUNET_assert (pos->ai_family == AF_INET6);
+ GNUNET_assert (pos->ai_addrlen == sizeof (struct sockaddr_in6));
+ saddrlens[i] = pos->ai_addrlen;
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+ memcpy (saddrs[i], pos->ai_addr, saddrlens[i]);
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
+ }
+ i++;
+ }
+ GNUNET_free (hostname);
+ freeaddrinfo (res);
+ resi = i;
+ }
+ else
+ {
+ /* will bind against everything, just set port */
+ if (disablev6)
+ {
+ /* V4-only */
+ resi = 1;
+ if (NULL != unixpath)
+ resi++;
+ i = 0;
+ saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
+ if (NULL != unixpath)
+ {
+ add_unixpath (saddrs, saddrlens, unixpath);
+ i++;
+ }
+ saddrlens[i] = sizeof (struct sockaddr_in);
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i];
+#endif
+ ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
+ ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
+ }
+ else
+ {
+ /* dual stack */
+ resi = 2;
+ if (NULL != unixpath)
+ resi++;
+ saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *));
+ saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t));
+ i = 0;
+ if (NULL != unixpath)
+ {
+ add_unixpath (saddrs, saddrlens, unixpath);
+ i++;
+ }
+ saddrlens[i] = sizeof (struct sockaddr_in6);
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0];
+#endif
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6;
+ ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port);
+ i++;
+ saddrlens[i] = sizeof (struct sockaddr_in);
+ saddrs[i] = GNUNET_malloc (saddrlens[i]);
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1];
+#endif
+ ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET;
+ ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port);
+ }
+ }
+ GNUNET_free_non_null (unixpath);
+ *addrs = saddrs;
+ *addr_lens = saddrlens;
+ return resi;
+}
+
+
+#ifdef MINGW
+/**
+ * @return GNUNET_YES if ok, GNUNET_NO if not ok (must bind yourself),
+ * and GNUNET_SYSERR on error.
+ */
+static int
+receive_sockets_from_parent (struct GNUNET_SERVICE_Context *sctx)
+{
+ const char *env_buf;
+ int fail;
+ uint64_t count, i;
+ HANDLE lsocks_pipe;
+
+ env_buf = getenv ("GNUNET_OS_READ_LSOCKS");
+ if ((env_buf == NULL) || (strlen (env_buf) <= 0))
+ {
+ return GNUNET_NO;
+ }
+ /* Using W32 API directly here, because this pipe will
+ * never be used outside of this function, and it's just too much of a bother
+ * to create a GNUnet API that boxes a HANDLE (the way it is done with socks)
+ */
+ lsocks_pipe = (HANDLE) strtoul (env_buf, NULL, 10);
+ if (lsocks_pipe == 0 || lsocks_pipe == INVALID_HANDLE_VALUE)
+ return GNUNET_NO;
+
+ fail = 1;
+ do
+ {
+ int ret;
+ int fail2;
+ DWORD rd;
+
+ ret = ReadFile (lsocks_pipe, &count, sizeof (count), &rd, NULL);
+ if (ret == 0 || rd != sizeof (count) || count == 0)
+ break;
+ sctx->lsocks =
+ GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (count + 1));
+
+ fail2 = 1;
+ for (i = 0; i < count; i++)
+ {
+ WSAPROTOCOL_INFOA pi;
+ uint64_t size;
+ SOCKET s;
+ ret = ReadFile (lsocks_pipe, &size, sizeof (size), &rd, NULL);
+ if (ret == 0 || rd != sizeof (size) || size != sizeof (pi))
+ break;
+ ret = ReadFile (lsocks_pipe, &pi, sizeof (pi), &rd, NULL);
+ if (ret == 0 || rd != sizeof (pi))
+ break;
+ s = WSASocketA (pi.iAddressFamily, pi.iSocketType, pi.iProtocol, &pi, 0, WSA_FLAG_OVERLAPPED);
+ sctx->lsocks[i] = GNUNET_NETWORK_socket_box_native (s);
+ if (sctx->lsocks[i] == NULL)
+ break;
+ else if (i == count - 1)
+ fail2 = 0;
+ }
+ if (fail2)
+ break;
+ sctx->lsocks[count] = NULL;
+ fail = 0;
+ }
+ while (fail);
+
+ CloseHandle (lsocks_pipe);
+
+ if (fail)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not access a pre-bound socket, will try to bind myself\n"));
+ for (i = 0; i < count && sctx->lsocks[i] != NULL; i++)
+ GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[i]));
+ GNUNET_free_non_null (sctx->lsocks);
+ sctx->lsocks = NULL;
+ return GNUNET_NO;
+ }
+
+ return GNUNET_YES;
+}
+#endif
+
+
+/**
+ * Setup addr, addrlen, idle_timeout
+ * based on configuration!
+ *
+ * Configuration may specify:
+ * - PORT (where to bind to for TCP)
+ * - UNIXPATH (where to bind to for UNIX domain sockets)
+ * - TIMEOUT (after how many ms does an inactive service timeout);
+ * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack)
+ * - 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)
+{
+ struct GNUNET_TIME_Relative idleout;
+ int tolerant;
+
+#ifndef MINGW
+ const char *lpid;
+ unsigned int pid;
+ const char *nfds;
+ unsigned int cnt;
+ int flags;
+#endif
+
+ if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->serviceName, "TIMEOUT"))
+ {
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_time (sctx->cfg, sctx->serviceName,
+ "TIMEOUT", &idleout))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Specified value for `%s' of service `%s' is invalid\n"),
+ "TIMEOUT", sctx->serviceName);
+ return GNUNET_SYSERR;
+ }
+ sctx->timeout = idleout;
+ }
+ else
+ sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+
+ if (GNUNET_CONFIGURATION_have_value
+ (sctx->cfg, sctx->serviceName, "TOLERANT"))
+ {
+ if (GNUNET_SYSERR ==
+ (tolerant =
+ GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName,
+ "TOLERANT")))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("Specified value for `%s' of service `%s' is invalid\n"),
+ "TOLERANT", sctx->serviceName);
+ return GNUNET_SYSERR;
+ }
+ }
+ else
+ tolerant = GNUNET_NO;
+
+#ifndef MINGW
+ errno = 0;
+ if ((NULL != (lpid = getenv ("LISTEN_PID"))) &&
+ (1 == sscanf (lpid, "%u", &pid)) && (getpid () == (pid_t) pid) &&
+ (NULL != (nfds = getenv ("LISTEN_FDS"))) &&
+ (1 == sscanf (nfds, "%u", &cnt)) && (cnt > 0) && (cnt < FD_SETSIZE) &&
+ (cnt + 4 < FD_SETSIZE))
+ {
+ sctx->lsocks =
+ GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (cnt + 1));
+ while (0 < cnt--)
+ {
+ flags = fcntl (3 + cnt, F_GETFD);
+ if ((flags < 0) || (0 != (flags & FD_CLOEXEC)) ||
+ (NULL ==
+ (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt))))
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _
+ ("Could not access pre-bound socket %u, will try to bind myself\n"),
+ (unsigned int) 3 + cnt);
+ cnt++;
+ while (sctx->lsocks[cnt] != NULL)
+ GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++]));
+ GNUNET_free (sctx->lsocks);
+ sctx->lsocks = NULL;
+ break;
+ }
+ }
+ unsetenv ("LISTEN_PID");
+ unsetenv ("LISTEN_FDS");
+ }
+#else
+ if (getenv ("GNUNET_OS_READ_LSOCKS") != NULL)
+ {
+ receive_sockets_from_parent (sctx);
+ putenv ("GNUNET_OS_READ_LSOCKS=");
+ }
+#endif
+
+ if ((sctx->lsocks == NULL) &&
+ (GNUNET_SYSERR ==
+ GNUNET_SERVICE_get_server_addresses (sctx->serviceName, sctx->cfg,
+ &sctx->addrs, &sctx->addrlens)))
+ return GNUNET_SYSERR;
+ sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES;
+ sctx->match_uid =
+ GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName,
+ "UNIX_MATCH_UID");
+ sctx->match_gid =
+ GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->serviceName,
+ "UNIX_MATCH_GID");
+ process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM");
+ process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM");
+ process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6");
+ process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6");
+
+ 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))
+ {
+ 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)
+ {
+ 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))
+ 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;
+}
+
+
+/**
+ * Task run during shutdown.
+ *
+ * @param cls unused
+ * @param tc unused
+ */
+static void
+shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_SERVER_Handle *server = cls;
+
+ GNUNET_SERVER_destroy (server);
+}
+
+
+/**
+ * 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;
+
+ GNUNET_RESOLVER_connect (sctx->cfg);
+ if (sctx->lsocks != NULL)
+ sctx->server =
+ GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks,
+ sctx->timeout, sctx->require_found);
+ else
+ sctx->server =
+ GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens,
+ sctx->timeout, sctx->require_found);
+ if (sctx->server == NULL)
+ {
+ if (sctx->addrs != NULL)
+ {
+ i = 0;
+ while (sctx->addrs[i] != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Failed to start `%s' at `%s'\n"),
+ sctx->serviceName, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
+ i++;
+ }
+ }
+ sctx->ret = GNUNET_SYSERR;
+ return;
+ }
+ if (0 == (sctx->options & GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN))
+ {
+ /* install a task that will kill the server
+ * process if the scheduler ever gets a shutdown signal */
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task,
+ sctx->server);
+ }
+ 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 ());
+ }
+ if (sctx->addrs != NULL)
+ {
+ i = 0;
+ while (sctx->addrs[i] != NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Service `%s' runs at %s\n"),
+ sctx->serviceName, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i]));
+ i++;
+ }
+ }
+ sctx->task (sctx->task_cls, sctx->server, sctx->cfg);
+}
+
+
+/**
+ * Detach from terminal.
+ */
+static int
+detach_terminal (struct GNUNET_SERVICE_Context *sctx)
+{
+#ifndef MINGW
+ pid_t pid;
+ int nullfd;
+ int filedes[2];
+
+ if (0 != PIPE (filedes))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe");
+ return GNUNET_SYSERR;
+ }
+ pid = fork ();
+ if (pid < 0)
+ {
+ 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)))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "read");
+ fflush (stdout);
+ switch (c)
+ {
+ case '.':
+ exit (0);
+ case 'I':
+ LOG (GNUNET_ERROR_TYPE_INFO, _("Service process failed to initialize\n"));
+ break;
+ case 'S':
+ LOG (GNUNET_ERROR_TYPE_INFO,
+ _("Service process could not initialize server function\n"));
+ break;
+ case 'X':
+ 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 = 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))
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2");
+ (void) CLOSE (nullfd);
+ return GNUNET_SYSERR;
+ }
+ (void) CLOSE (nullfd);
+ /* Detach from controlling terminal */
+ pid = setsid ();
+ if (pid == -1)
+ 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)
+ {
+ 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)))
+ {
+ 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))
+ 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 opt service options
+ * @param task main task of the service
+ * @param task_cls closure for task
+ * @return GNUNET_SYSERR on error, GNUNET_OK
+ * if we shutdown nicely
+ */
+int
+GNUNET_SERVICE_run (int argc, char *const *argv, const char *serviceName,
+ enum GNUNET_SERVICE_Options opt, GNUNET_SERVICE_Main task,
+ void *task_cls)
+{
+#define HANDLE_ERROR do { GNUNET_break (0); goto shutdown; } while (0)
+
+ int err;
+ char *cfg_fn;
+ char *loglev;
+ char *logfile;
+ int do_daemonize;
+ unsigned int i;
+ unsigned long long skew_offset;
+ unsigned long long skew_variance;
+ long long clock_offset;
+ struct GNUNET_SERVICE_Context sctx;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+
+ 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
+ };
+ err = 1;
+ do_daemonize = 0;
+ logfile = NULL;
+ loglev = NULL;
+ cfg_fn = GNUNET_strdup (GNUNET_DEFAULT_USER_CONFIG_FILE);
+ memset (&sctx, 0, sizeof (sctx));
+ sctx.options = opt;
+ sctx.ready_confirm_fd = -1;
+ sctx.ret = GNUNET_OK;
+ sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL;
+ sctx.task = task;
+ sctx.task_cls = task_cls;
+ sctx.serviceName = serviceName;
+ sctx.cfg = cfg = GNUNET_CONFIGURATION_create ();
+ /* setup subsystems */
+ if (GNUNET_SYSERR ==
+ GNUNET_GETOPT_run (serviceName, service_options, argc, argv))
+ goto shutdown;
+ if (GNUNET_OK != GNUNET_log_setup (serviceName, loglev, logfile))
+ HANDLE_ERROR;
+ if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfg, cfg_fn))
+ goto shutdown;
+ if (GNUNET_OK != setup_service (&sctx))
+ goto shutdown;
+ if ((do_daemonize == 1) && (GNUNET_OK != detach_terminal (&sctx)))
+ HANDLE_ERROR;
+ if (GNUNET_OK != set_user_id (&sctx))
+ goto shutdown;
+#if DEBUG_SERVICE
+ LOG (GNUNET_ERROR_TYPE_DEBUG,
+ "Service `%s' runs with configuration from `%s'\n", serviceName, cfg_fn);
+#endif
+ if ((GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING",
+ "SKEW_OFFSET", &skew_offset)) &&
+ (GNUNET_OK ==
+ GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING",
+ "SKEW_VARIANCE", &skew_variance)))
+ {
+ clock_offset = skew_offset - skew_variance;
+ GNUNET_TIME_set_offset (clock_offset);
+#if DEBUG_SERVICE
+ LOG (GNUNET_ERROR_TYPE_DEBUG, "Skewing clock by %dll ms\n", clock_offset);
+#endif
+ }
+ /* actually run service */
+ err = 0;
+ GNUNET_SCHEDULER_run (&service_task, &sctx);
+
+ /* shutdown */
+ if ((do_daemonize == 1) && (sctx.server != NULL))
+ pid_file_delete (&sctx);
+ GNUNET_free_non_null (sctx.my_handlers);
+
+shutdown:
+ if (sctx.ready_confirm_fd != -1)
+ {
+ if (1 != WRITE (sctx.ready_confirm_fd, err ? "I" : "S", 1))
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "write");
+ GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd));
+ }
+
+ GNUNET_CONFIGURATION_destroy (cfg);
+ i = 0;
+ if (sctx.addrs != NULL)
+ while (sctx.addrs[i] != NULL)
+ GNUNET_free (sctx.addrs[i++]);
+ GNUNET_free_non_null (sctx.addrs);
+ GNUNET_free_non_null (sctx.addrlens);
+ GNUNET_free_non_null (logfile);
+ GNUNET_free_non_null (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 err ? GNUNET_SYSERR : sctx.ret;
+}
+
+
+/**
+ * Run a service startup sequence within an existing
+ * initialized system.
+ *
+ * @param serviceName our service name
+ * @param cfg configuration to use
+ * @return NULL on error, service handle
+ */
+struct GNUNET_SERVICE_Context *
+GNUNET_SERVICE_start (const char *serviceName,
+ const 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->serviceName = serviceName;
+ sctx->cfg = cfg;
+
+ /* setup subsystems */
+ if (GNUNET_OK != setup_service (sctx))
+ {
+ GNUNET_SERVICE_stop (sctx);
+ return NULL;
+ }
+ if (sctx->lsocks != NULL)
+ sctx->server =
+ GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks,
+ sctx->timeout, sctx->require_found);
+ else
+ sctx->server =
+ GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens,
+ sctx->timeout, sctx->require_found);
+
+ if (NULL == sctx->server)
+ {
+ 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 sctx the service context returned from the start function
+ */
+void
+GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx)
+{
+ unsigned int i;
+
+ if (NULL != sctx->server)
+ GNUNET_SERVER_destroy (sctx->server);
+ GNUNET_free_non_null (sctx->my_handlers);
+ if (sctx->addrs != NULL)
+ {
+ i = 0;
+ while (sctx->addrs[i] != NULL)
+ GNUNET_free (sctx->addrs[i++]);
+ GNUNET_free (sctx->addrs);
+ }
+ GNUNET_free_non_null (sctx->addrlens);
+ 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 0000000..c3bb718
--- /dev/null
+++ b/src/util/signal.c
@@ -0,0 +1,98 @@
+/*
+ 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"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+
+struct GNUNET_SIGNAL_Context
+{
+ int sig;
+
+ GNUNET_SIGNAL_Handler method;
+
+#ifndef MINGW
+ struct sigaction oldsig;
+#endif
+};
+
+#ifdef WINDOWS
+GNUNET_SIGNAL_Handler w32_sigchld_handler = NULL;
+#endif
+
+struct GNUNET_SIGNAL_Context *
+GNUNET_SIGNAL_handler_install (int signum, 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 = signum;
+ ret->method = handler;
+#ifndef MINGW
+ memset (&sig, 0, sizeof (sig));
+ 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 (signum, &sig, &ret->oldsig);
+#else
+ if (signum == GNUNET_SIGCHLD)
+ w32_sigchld_handler = handler;
+ else
+ {
+ __p_sig_fn_t sigret = signal (signum, (__p_sig_fn_t) handler);
+
+ if (sigret == SIG_ERR)
+ {
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("signal (%d, %p) returned %d.\n"),
+ signum, handler, sigret);
+ }
+ }
+#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 0000000..8000a93
--- /dev/null
+++ b/src/util/strings.c
@@ -0,0 +1,633 @@
+/*
+ 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
+#include <iconv.h>
+#endif
+#include "gnunet_common.h"
+#include "gnunet_strings_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+#define LOG_STRERROR(kind,syscall) GNUNET_log_from_strerror (kind, "util", syscall)
+
+
+/**
+ * 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.
+ *
+ * @param buffer the buffer to fill with strings, can
+ * be NULL in which case only the necessary
+ * amount of space will be calculated
+ * @param size number of bytes available in buffer
+ * @param count number of strings that follow
+ * @param ... count 0-terminated strings to copy to buffer
+ * @return number of bytes written to the buffer
+ * (or number of bytes that would have been written)
+ */
+size_t
+GNUNET_STRINGS_buffer_fill (char *buffer, size_t size, unsigned int count, ...)
+{
+ size_t needed;
+ size_t 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, size_t 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.
+ *
+ * @param size number of bytes
+ * @return fancy representation of the size (possibly rounded) for humans
+ */
+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 = "KiB";
+ if (size > 5 * 1024)
+ {
+ size = size / 1024;
+ unit = "MiB";
+ if (size > 5 * 1024)
+ {
+ size = size / 1024;
+ unit = "GiB";
+ if (size > 5 * 1024)
+ {
+ size = size / 1024;
+ unit = "TiB";
+ }
+ }
+ }
+ }
+ ret = GNUNET_malloc (32);
+ GNUNET_snprintf (ret, 32, "%llu %s", size, unit);
+ return ret;
+}
+
+
+/**
+ * Convert a given fancy human-readable size to bytes.
+ *
+ * @param fancy_size human readable string (i.e. 1 MB)
+ * @param size set to the size in bytes
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_STRINGS_fancy_size_to_bytes (const char *fancy_size,
+ unsigned long long *size)
+{
+ struct
+ {
+ const char *name;
+ unsigned long long value;
+ } table[] =
+ {
+ {
+ "B", 1},
+ {
+ "KiB", 1024},
+ {
+ "kB", 1000},
+ {
+ "MiB", 1024 * 1024},
+ {
+ "MB", 1000 * 1000},
+ {
+ "GiB", 1024 * 1024 * 1024},
+ {
+ "GB", 1000 * 1000 * 1000},
+ {
+ "TiB", 1024LL * 1024LL * 1024LL * 1024LL},
+ {
+ "TB", 1000LL * 1000LL * 1000LL * 1024LL},
+ {
+ "PiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL},
+ {
+ "PB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL},
+ {
+ "EiB", 1024LL * 1024LL * 1024LL * 1024LL * 1024LL * 1024LL},
+ {
+ "EB", 1000LL * 1000LL * 1000LL * 1024LL * 1000LL * 1000LL},
+ {
+ NULL, 0}
+ };
+ unsigned long long ret;
+ char *in;
+ const char *tok;
+ unsigned long long last;
+ unsigned int i;
+
+ ret = 0;
+ last = 0;
+ in = GNUNET_strdup (fancy_size);
+ for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
+ {
+ i = 0;
+ while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
+ i++;
+ if (table[i].name != NULL)
+ last *= table[i].value;
+ else
+ {
+ ret += last;
+ last = 0;
+ if (1 != sscanf (tok, "%llu", &last))
+ {
+ GNUNET_free (in);
+ return GNUNET_SYSERR; /* expected number */
+ }
+ }
+ }
+ ret += last;
+ *size = ret;
+ GNUNET_free (in);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Convert a given fancy human-readable time to our internal
+ * representation.
+ *
+ * @param fancy_size human readable string (i.e. 1 minute)
+ * @param rtime set to the relative time
+ * @return GNUNET_OK on success, GNUNET_SYSERR on error
+ */
+int
+GNUNET_STRINGS_fancy_time_to_relative (const char *fancy_size,
+ struct GNUNET_TIME_Relative *rtime)
+{
+ struct
+ {
+ const char *name;
+ unsigned long long value;
+ } table[] =
+ {
+ {
+ "ms", 1},
+ {
+ "s", 1000},
+ {
+ "\"", 1000},
+ {
+ "min", 60 * 1000},
+ {
+ "minutes", 60 * 1000},
+ {
+ "'", 60 * 1000},
+ {
+ "h", 60 * 60 * 1000},
+ {
+ "d", 24 * 60 * 60 * 1000},
+ {
+ "a", 31557600 /* year */ },
+ {
+ NULL, 0}
+ };
+ unsigned long long ret;
+ char *in;
+ const char *tok;
+ unsigned long long last;
+ unsigned int i;
+
+ if ((0 == strcasecmp (fancy_size, "infinity")) ||
+ (0 == strcasecmp (fancy_size, "forever")))
+ {
+ *rtime = GNUNET_TIME_UNIT_FOREVER_REL;
+ return GNUNET_OK;
+ }
+ ret = 0;
+ last = 0;
+ in = GNUNET_strdup (fancy_size);
+ for (tok = strtok (in, " "); tok != NULL; tok = strtok (NULL, " "))
+ {
+ i = 0;
+ while ((table[i].name != NULL) && (0 != strcasecmp (table[i].name, tok)))
+ i++;
+ if (table[i].name != NULL)
+ last *= table[i].value;
+ else
+ {
+ ret += last;
+ last = 0;
+ if (1 != sscanf (tok, "%llu", &last))
+ {
+ GNUNET_free (in);
+ return GNUNET_SYSERR; /* expected number */
+ }
+ }
+ }
+ ret += last;
+ rtime->rel_value = (uint64_t) ret;
+ GNUNET_free (in);
+ return GNUNET_OK;
+}
+
+/**
+ * Convert the len characters long character sequence
+ * given in input that is in the given input charset
+ * to a string in given output charset.
+ * @return the converted string (0-terminated),
+ * if conversion fails, a copy of the orignal
+ * string is returned.
+ */
+char *
+GNUNET_STRINGS_conv (const char *input, size_t len, const char *input_charset, const char *output_charset)
+{
+ char *ret;
+
+#if ENABLE_NLS && HAVE_ICONV
+ size_t tmpSize;
+ size_t finSize;
+ char *tmp;
+ char *itmp;
+ iconv_t cd;
+
+ cd = iconv_open (output_charset, input_charset);
+ if (cd == (iconv_t) - 1)
+ {
+ LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "iconv_open");
+ LOG (GNUNET_ERROR_TYPE_WARNING, _("Character sets requested were `%s'->`%s'\n"),
+ input_charset, output_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,
+#if FREEBSD || DARWIN || WINDOWS
+ (const char **) &input,
+#else
+ (char **) &input,
+#endif
+ &len, &itmp, &finSize) == SIZE_MAX)
+ {
+ 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))
+ 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
+}
+
+
+/**
+ * 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)
+{
+ return GNUNET_STRINGS_conv (input, len, charset, "UTF-8");
+}
+
+/**
+ * Convert the len bytes-long UTF-8 string
+ * given in input to the given charset.
+
+ * @return the converted string (0-terminated),
+ * if conversion fails, a copy of the orignal
+ * string is returned.
+ */
+char *
+GNUNET_STRINGS_from_utf8 (const char *input, size_t len, const char *charset)
+{
+ return GNUNET_STRINGS_conv (input, len, "UTF-8", 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)
+{
+ 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)
+ {
+ 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)
+ {
+ 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);
+ 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);
+ 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
+ * @return time as human-readable string
+ */
+char *
+GNUNET_STRINGS_relative_time_to_string (struct GNUNET_TIME_Relative delta)
+{
+ const char *unit = _( /* time unit */ "ms");
+ char *ret;
+ uint64_t dval = delta.rel_value;
+
+ if (delta.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value)
+ return GNUNET_strdup (_("eternity"));
+ if (dval > 5 * 1000)
+ {
+ dval = dval / 1000;
+ unit = _( /* time unit */ "s");
+ if (dval > 5 * 60)
+ {
+ dval = dval / 60;
+ unit = _( /* time unit */ "m");
+ if (dval > 5 * 60)
+ {
+ dval = dval / 60;
+ unit = _( /* time unit */ "h");
+ if (dval > 5 * 24)
+ {
+ dval = dval / 24;
+ unit = _( /* time unit */ " days");
+ }
+ }
+ }
+ }
+ GNUNET_asprintf (&ret, "%llu %s", dval, unit);
+ return ret;
+}
+
+
+/**
+ * "man ctime_r", except for GNUnet time; also, unlike ctime, the
+ * return value does not include the newline character.
+ *
+ * @param t time to convert
+ * @return absolute time in human-readable format
+ */
+char *
+GNUNET_STRINGS_absolute_time_to_string (struct GNUNET_TIME_Absolute t)
+{
+ time_t tt;
+ char *ret;
+
+ if (t.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
+ return GNUNET_strdup (_("end of time"));
+ tt = t.abs_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;
+}
+
+
+/**
+ * "man basename"
+ * Returns a pointer to a part of filename (allocates nothing)!
+ *
+ * @param filename filename to extract basename from
+ * @return short (base) name of the file (that is, everything following the
+ * last directory separator in filename. If filename ends with a
+ * directory separator, the result will be a zero-length string.
+ * If filename has no directory separators, the result is filename
+ * itself.
+ */
+const char *
+GNUNET_STRINGS_get_short_name (const char *filename)
+{
+ const char *short_fn = filename;
+ const char *ss;
+ while (NULL != (ss = strstr (short_fn, DIR_SEPARATOR_STR))
+ && (ss[1] != '\0'))
+ short_fn = 1 + ss;
+ return short_fn;
+}
+
+/* end of strings.c */
diff --git a/src/util/test_bio.c b/src/util/test_bio.c
new file mode 100644
index 0000000..9b18258
--- /dev/null
+++ b/src/util/test_bio.c
@@ -0,0 +1,417 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_bio.c
+ * @brief testcase for the buffered IO module
+ * @author Ji Lu
+ */
+
+
+#include "platform.h"
+#include "gnunet_util_lib.h"
+#define TESTSTRING "testString"
+#define TESTNUMBER64 ((int64_t)100000L)
+
+static int
+test_normal_rw ()
+{
+ char *msg;
+ int64_t testNum;
+ char *readResultString;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ struct GNUNET_CONTAINER_MetaData *metaDataW;
+ struct GNUNET_CONTAINER_MetaData *metaDataR;
+
+ metaDataW = GNUNET_CONTAINER_meta_data_create ();
+ metaDataR = NULL;
+ GNUNET_CONTAINER_meta_data_add_publication_date (metaDataW);
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_string (fileW, TESTSTRING));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_meta_data (fileW, metaDataW));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int64 (fileW, TESTNUMBER64));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ readResultString = NULL;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_BIO_read_string (fileR, "Read string error",
+ &readResultString, 200));
+ GNUNET_assert (NULL != readResultString);
+ GNUNET_assert (0 == strcmp (TESTSTRING, readResultString));
+ GNUNET_free (readResultString);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_BIO_read_meta_data (fileR, "Read meta error",
+ &metaDataR));
+ GNUNET_assert (GNUNET_YES ==
+ GNUNET_CONTAINER_meta_data_test_equal (metaDataR, metaDataW));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_read_int64 (fileR, &testNum));
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_CONTAINER_meta_data_destroy (metaDataW);
+ GNUNET_CONTAINER_meta_data_destroy (metaDataR);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+test_nullstring_rw ()
+{
+ char *msg;
+ char *readResultString = (char *) "not null";
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_string (fileW, NULL));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_BIO_read_string (fileR, "Read string error",
+ &readResultString, 200));
+ GNUNET_assert (NULL == readResultString);
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_free (fileName);
+
+ return 0;
+}
+
+static int
+test_emptystring_rw ()
+{
+ char *msg;
+ char *readResultString;
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_string (fileW, ""));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ readResultString = NULL;
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_BIO_read_string (fileR, "Read string error",
+ &readResultString, 200));
+ GNUNET_free (readResultString);
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+test_bigstring_rw ()
+{
+ char *msg;
+ char *readResultString;
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_string (fileW, TESTSTRING));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ readResultString = NULL;
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_string (fileR, "Read string error",
+ &readResultString, 1));
+ GNUNET_assert (NULL == readResultString);
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+test_bigmeta_rw ()
+{
+ char *msg;
+ static char meta[1024 * 1024 * 10];
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+ struct GNUNET_CONTAINER_MetaData *metaDataR;
+
+ memset (meta, 'b', sizeof (meta));
+ meta[sizeof (meta) - 1] = '\0';
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int32 (fileW, sizeof (meta)));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write (fileW, meta, sizeof (meta)));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ metaDataR = NULL;
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_meta_data (fileR, "Read meta error",
+ &metaDataR));
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_assert (NULL == metaDataR);
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+test_directory_r ()
+{
+#if LINUX
+ char *msg;
+ char readResult[200];
+ struct GNUNET_BIO_ReadHandle *fileR;
+
+ fileR = GNUNET_BIO_read_open ("/dev");
+ GNUNET_assert (NULL != fileR);
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read (fileR, "Read error", readResult,
+ sizeof (readResult)));
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+#endif
+ return 0;
+}
+
+static int
+test_nullfile_rw ()
+{
+ static char fileNameNO[102401];
+ struct GNUNET_BIO_WriteHandle *fileWNO;
+ struct GNUNET_BIO_ReadHandle *fileRNO;
+
+ memset (fileNameNO, 'a', sizeof (fileNameNO));
+ fileNameNO[sizeof (fileNameNO) - 1] = '\0';
+
+ GNUNET_log_skip (1, GNUNET_NO);
+ fileWNO = GNUNET_BIO_write_open (fileNameNO);
+ GNUNET_log_skip (0, GNUNET_YES);
+ GNUNET_assert (NULL == fileWNO);
+
+ GNUNET_log_skip (1, GNUNET_NO);
+ fileRNO = GNUNET_BIO_read_open (fileNameNO);
+ GNUNET_log_skip (0, GNUNET_YES);
+ GNUNET_assert (NULL == fileRNO);
+ return 0;
+}
+
+
+static int
+test_fullfile_rw ()
+{
+#ifdef LINUX
+ /* /dev/full only seems to exist on Linux */
+ char *msg;
+ int64_t testNum;
+ char *readResultString;
+ char readResult[200];
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ struct GNUNET_CONTAINER_MetaData *metaDataW;
+ struct GNUNET_CONTAINER_MetaData *metaDataR;
+
+ metaDataW = GNUNET_CONTAINER_meta_data_create ();
+ GNUNET_CONTAINER_meta_data_add_publication_date (metaDataW);
+
+ fileW = GNUNET_BIO_write_open ("/dev/full");
+ GNUNET_assert (NULL != fileW);
+ (void) GNUNET_BIO_write (fileW, TESTSTRING, strlen (TESTSTRING));
+ (void) GNUNET_BIO_write_string (fileW, TESTSTRING);
+ (void) GNUNET_BIO_write_meta_data (fileW, metaDataW);
+ GNUNET_assert (GNUNET_SYSERR == GNUNET_BIO_write_close (fileW));
+ GNUNET_CONTAINER_meta_data_destroy (metaDataW);
+
+ fileW = GNUNET_BIO_write_open ("/dev/full");
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_SYSERR == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open ("/dev/null");
+ GNUNET_assert (NULL != fileR);
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read (fileR, "Read error", readResult,
+ sizeof (readResult)));
+ readResultString = NULL;
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_string (fileR, "Read string error",
+ &readResultString, 200));
+ GNUNET_assert (NULL == readResultString);
+ GNUNET_assert (GNUNET_SYSERR == GNUNET_BIO_read_int64 (fileR, &testNum));
+ metaDataR = NULL;
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_meta_data (fileR, "Read meta error",
+ &metaDataR));
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+ GNUNET_assert (NULL == metaDataR);
+#endif
+ return 0;
+}
+
+static int
+test_fakestring_rw ()
+{
+ char *msg;
+ int32_t tmpInt = 2;
+ char *readResult;
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int32 (fileW, tmpInt));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_string (fileR, "Read string error",
+ &readResult, 200));
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+test_fakemeta_rw ()
+{
+ char *msg;
+ int32_t tmpInt = 2;
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+ struct GNUNET_CONTAINER_MetaData *metaDataR;
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int32 (fileW, tmpInt));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ metaDataR = NULL;
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_meta_data (fileR, "Read meta error",
+ &metaDataR));
+ GNUNET_assert (NULL == metaDataR);
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+test_fakebigmeta_rw ()
+{
+ char *msg;
+ int32_t tmpInt = 1024 * 1024 * 10;
+ struct GNUNET_BIO_WriteHandle *fileW;
+ struct GNUNET_BIO_ReadHandle *fileR;
+ char *fileName = GNUNET_DISK_mktemp ("gnunet_bio");
+ struct GNUNET_CONTAINER_MetaData *metaDataR;
+
+ fileW = GNUNET_BIO_write_open (fileName);
+ GNUNET_assert (NULL != fileW);
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_int32 (fileW, tmpInt));
+ GNUNET_assert (GNUNET_OK == GNUNET_BIO_write_close (fileW));
+
+ fileR = GNUNET_BIO_read_open (fileName);
+ GNUNET_assert (NULL != fileR);
+ metaDataR = NULL;
+ GNUNET_assert (GNUNET_SYSERR ==
+ GNUNET_BIO_read_meta_data (fileR, "Read meta error",
+ &metaDataR));
+ msg = NULL;
+ GNUNET_BIO_read_close (fileR, &msg);
+ GNUNET_free (msg);
+ GNUNET_assert (GNUNET_OK == GNUNET_DISK_directory_remove (fileName));
+ GNUNET_assert (NULL == metaDataR);
+ GNUNET_free (fileName);
+ return 0;
+}
+
+static int
+check_string_rw ()
+{
+ GNUNET_assert (0 == test_nullstring_rw ());
+ GNUNET_assert (0 == test_emptystring_rw ());
+ GNUNET_assert (0 == test_bigstring_rw ());
+ GNUNET_assert (0 == test_fakestring_rw ());
+ return 0;
+}
+
+static int
+check_metadata_rw ()
+{
+ GNUNET_assert (0 == test_fakebigmeta_rw ());
+ GNUNET_assert (0 == test_fakemeta_rw ());
+ GNUNET_assert (0 == test_bigmeta_rw ());
+ return 0;
+}
+
+static int
+check_file_rw ()
+{
+ GNUNET_assert (0 == test_normal_rw ());
+ GNUNET_assert (0 == test_nullfile_rw ());
+ GNUNET_assert (0 == test_fullfile_rw ());
+ GNUNET_assert (0 == test_directory_r ());
+ return 0;
+}
+
+int
+main (int argc, char *argv[])
+{
+ GNUNET_log_setup ("test-bio", "WARNING", NULL);
+ GNUNET_assert (0 == check_file_rw ());
+ GNUNET_assert (0 == check_metadata_rw ());
+ GNUNET_assert (0 == check_string_rw ());
+ return 0;
+}
+
+/* end of test_bio.c */
diff --git a/src/util/test_client.c b/src/util/test_client.c
new file mode 100644
index 0000000..f9d961a
--- /dev/null
+++ b/src/util/test_client.c
@@ -0,0 +1,210 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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);
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Message bounced back to client\n");
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+/**
+ * Callback that just bounces the message back to the sender.
+ */
+static void
+echo_cb (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ struct CopyContext *cc;
+ struct GNUNET_MessageHeader *cpy;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Receiving message from client, bouncing back\n");
+ 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,
+ &copy_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 */
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Receiving bounce, checking content\n");
+ msg.type = htons (MY_TYPE);
+ msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ GNUNET_assert (0 == memcmp (got, &msg, sizeof (struct GNUNET_MessageHeader)));
+ GNUNET_CLIENT_disconnect (client, GNUNET_YES);
+ 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 (struct GNUNET_MessageHeader));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Creating message for transmission\n");
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+static void
+task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct sockaddr_in sa;
+ struct sockaddr *sap[2];
+ socklen_t slens[2];
+
+ sap[0] = (struct sockaddr *) &sa;
+ slens[0] = sizeof (sa);
+ sap[1] = NULL;
+ slens[1] = 0;
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ server =
+ GNUNET_SERVER_create (NULL, NULL, sap, slens,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, 10000), 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 (MYNAME, cfg);
+ GNUNET_assert (client != NULL);
+ GNUNET_assert (NULL !=
+ GNUNET_CLIENT_notify_transmit_ready (client,
+ sizeof (struct
+ GNUNET_MessageHeader),
+ GNUNET_TIME_UNIT_SECONDS,
+ GNUNET_NO, &make_msg,
+ NULL));
+ GNUNET_CLIENT_receive (client, &recv_bounce, cls,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, 10000));
+}
+
+
+/**
+ * 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");
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "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",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ 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 0000000..438d397
--- /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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 0000000..a709abe
--- /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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 0000000..9345869
--- /dev/null
+++ b/src/util/test_common_logging.c
@@ -0,0 +1,99 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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;
+
+ if (0 != putenv ("GNUNET_FORCE_LOG="))
+ FPRINTF (stderr, "Failed to putenv: %s\n", strerror (errno));
+ GNUNET_log_setup ("test-common-logging", "DEBUG", "/dev/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_common_logging_dummy.c b/src/util/test_common_logging_dummy.c
new file mode 100644
index 0000000..a1f4799
--- /dev/null
+++ b/src/util/test_common_logging_dummy.c
@@ -0,0 +1,98 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_dummy.c
+ * @brief dummy labrat for the testcase for the logging module (runtime
+ * log level adjustment)
+ * @author LRN
+ */
+#include "platform.h"
+#undef GNUNET_EXTRA_LOGGING
+#define GNUNET_EXTRA_LOGGING GNUNET_YES
+
+#include "gnunet_common.h"
+#include "gnunet_time_lib.h"
+#include "gnunet_network_lib.h"
+
+/**
+ * Delay introduced between operations, useful for debugging.
+ */
+#define OUTPUT_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 0)
+
+static void
+my_log (void *ctx, enum GNUNET_ErrorType kind, const char *component,
+ const char *date, const char *msg)
+{
+ if (strncmp ("test-common-logging-dummy", component, 25) != 0)
+ return;
+ FPRINTF (stdout, "%s", msg);
+ fflush (stdout);
+}
+
+static int
+expensive_func ()
+{
+ return GNUNET_NETWORK_socket_select (NULL, NULL, NULL, OUTPUT_DELAY);
+}
+
+#define pr(kind,lvl) {\
+ struct GNUNET_TIME_Absolute t1, t2;\
+ t1 = GNUNET_TIME_absolute_get ();\
+ GNUNET_log (kind, "L%s %d\n", lvl, expensive_func());\
+ t2 = GNUNET_TIME_absolute_get ();\
+ printf ("1%s %llu\n", lvl,\
+ (unsigned long long) GNUNET_TIME_absolute_get_difference (t1, t2).rel_value); \
+}
+
+#define pr2(kind,lvl) {\
+ struct GNUNET_TIME_Absolute t1, t2;\
+ t1 = GNUNET_TIME_absolute_get ();\
+ GNUNET_log (kind, "L%s %d\n", lvl, expensive_func());\
+ t2 = GNUNET_TIME_absolute_get ();\
+ printf ("2%s %llu\n", lvl,\
+ (unsigned long long) GNUNET_TIME_absolute_get_difference (t1, t2).rel_value); \
+}
+
+int
+main (int argc, char *argv[])
+{
+ /* We set up logging with NULL level - will be overrided by
+ * GNUNET_LOG or GNUNET_FORCE_LOG at runtime.
+ */
+ GNUNET_log_setup ("test-common-logging-dummy", NULL, "/dev/null");
+ GNUNET_logger_add (&my_log, NULL);
+ pr (GNUNET_ERROR_TYPE_ERROR, "ERROR");
+ pr (GNUNET_ERROR_TYPE_WARNING, "WARNING");
+ pr (GNUNET_ERROR_TYPE_INFO, "INFO");
+ pr (GNUNET_ERROR_TYPE_DEBUG, "DEBUG");
+
+ /* We set up logging with WARNING level - will onle be overrided by
+ * GNUNET_FORCE_LOG at runtime.
+ */
+ GNUNET_log_setup ("test-common-logging-dummy", "WARNING", "/dev/null");
+ pr2 (GNUNET_ERROR_TYPE_ERROR, "ERROR");
+ pr2 (GNUNET_ERROR_TYPE_WARNING, "WARNING");
+ pr2 (GNUNET_ERROR_TYPE_INFO, "INFO");
+ pr2 (GNUNET_ERROR_TYPE_DEBUG, "DEBUG");
+ return 0;
+} /* end of main */
+
+/* end of test_common_logging_dummy.c */
diff --git a/src/util/test_common_logging_runtime_loglevels.c b/src/util/test_common_logging_runtime_loglevels.c
new file mode 100644
index 0000000..cdf1f66
--- /dev/null
+++ b/src/util/test_common_logging_runtime_loglevels.c
@@ -0,0 +1,384 @@
+/*
+ This file is part of GNUnet.
+ (C) 2011 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_runtime_loglevels.c
+ * @brief testcase for the logging module (runtime log level adjustment)
+ * @author LRN
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_network_lib.h"
+#include "gnunet_disk_lib.h"
+#include "gnunet_os_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+static int ok;
+static int phase = 0;
+
+static struct GNUNET_OS_Process *proc;
+
+/* Pipe to read from started processes stdout (on read end) */
+static struct GNUNET_DISK_PipeHandle *pipe_stdout;
+
+static GNUNET_SCHEDULER_TaskIdentifier die_task;
+
+static void
+runone (void);
+
+static void
+end_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Ending phase %d, ok is %d\n", phase,
+ ok);
+ if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
+ }
+ GNUNET_OS_process_wait (proc);
+ GNUNET_OS_process_close (proc);
+ proc = NULL;
+ GNUNET_DISK_pipe_close (pipe_stdout);
+ if (ok == 1)
+ {
+ if (phase < 9)
+ {
+ phase += 1;
+ runone ();
+ }
+ else
+ ok = 0;
+ }
+ else
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "failing\n");
+}
+
+static char *
+read_output_line (int phase_from1, int phase_to1, int phase_from2,
+ int phase_to2, char c, char *expect_level,
+ long delay_morethan, long delay_lessthan, int phase, char *p,
+ int *len, long *delay, char level[8])
+{
+ char *r = p;
+ char t[7];
+ int i, j, stop = 0;
+
+ j = 0;
+ int stage = 0;
+
+ if (!(phase >= phase_from1 && phase <= phase_to1) &&
+ !(phase >= phase_from2 && phase <= phase_to2))
+ return p;
+#if 0
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Trying to match '%c%s \\d\\r\\n' on %s\n", c, expect_level, p);
+#endif
+ for (i = 0; i < *len && !stop; i++)
+ {
+ switch (stage)
+ {
+ case 0: /* read first char */
+ if (r[i] != c)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Expected '%c', but got '%c'\n", c,
+ r[i]);
+ GNUNET_break (0);
+ return NULL;
+ }
+ stage += 1;
+ break;
+ case 1: /* read at most 7 char-long error level string, finished by ' ' */
+ if (r[i] == ' ')
+ {
+ level[j] = '\0';
+ stage += 1;
+ j = 0;
+ }
+ else if (i == 8)
+ {
+ GNUNET_break (0);
+ ok = 2;
+ return NULL;
+ }
+ else
+ level[j++] = r[i];
+ break;
+ case 2: /* read the delay, finished by '\n' */
+ t[j++] = r[i];
+#if WINDOWS
+ if (r[i] == '\r' && r[i + 1] == '\n')
+ {
+ i += 1;
+ t[j - 1] = '\0';
+ *delay = strtol (t, NULL, 10);
+ stop = 1;
+ }
+#else
+ if (r[i] == '\n')
+ {
+ t[j - 1] = '\0';
+ *delay = strtol (t, NULL, 10);
+ stop = 1;
+ }
+#endif
+ break;
+ }
+ }
+ if (!stop || strcmp (expect_level, level) != 0 || *delay < 0 || *delay > 1000
+ || (!((*delay < delay_lessthan) || !(*delay > delay_morethan)) && c != '1'
+ && c != '2'))
+ return NULL;
+ *len = *len - i;
+ return &r[i];
+}
+
+char buf[20 * 16];
+char *buf_ptr;
+int bytes;
+
+static void
+read_call (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_DISK_FileHandle *stdout_read_handle = cls;
+ char level[8];
+ long delay;
+ long delays[8];
+ int rd;
+
+ rd = GNUNET_DISK_file_read (stdout_read_handle, buf_ptr,
+ sizeof (buf) - bytes);
+ if (rd > 0)
+ {
+ buf_ptr += rd;
+ bytes += rd;
+#if VERBOSE
+ FPRINTF (stderr, "got %d bytes, reading more\n", rd);
+#endif
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ stdout_read_handle, &read_call,
+ (void *) stdout_read_handle);
+ return;
+ }
+
+#if VERBOSE
+ FPRINTF (stderr, "bytes is %d:%s\n", bytes, buf);
+#endif
+
+ /* +------CHILD OUTPUT--
+ * | SOFT HARD
+ * | E W I D E W I D
+ * | 0E * * *
+ * | 1W * * * *
+ * P 2I * * * * *
+ * H 3D * * * * * *
+ * A
+ * S 4E * *
+ * E 5W * * * *
+ * | 6I * * * * * *
+ * | 7D * * * * * * * *
+ * | 8 * * * *
+ * | 9 * * * *
+ */
+ char *p = buf;
+
+ if (bytes == 20 * 16 ||
+ !(p =
+ read_output_line (0, 3, 4, 9, 'L', "ERROR", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '1', "ERROR", 200, 400, phase, p, &bytes,
+ &delays[0], level)) ||
+ !(p =
+ read_output_line (1, 3, 5, 9, 'L', "WARNING", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '1', "WARNING", 200, 400, phase, p,
+ &bytes, &delays[1], level)) ||
+ !(p =
+ read_output_line (2, 3, 6, 7, 'L', "INFO", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '1', "INFO", 200, 400, phase, p, &bytes,
+ &delays[2], level)) ||
+ !(p =
+ read_output_line (3, 3, 7, 7, 'L', "DEBUG", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '1', "DEBUG", 200, 400, phase, p, &bytes,
+ &delays[3], level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, 'L', "ERROR", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '2', "ERROR", 200, 400, phase, p, &bytes,
+ &delays[4], level)) ||
+ !(p =
+ read_output_line (0, 3, 5, 9, 'L', "WARNING", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '2', "WARNING", 200, 400, phase, p,
+ &bytes, &delays[5], level)) ||
+ !(p =
+ read_output_line (-1, -1, 6, 7, 'L', "INFO", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '2', "INFO", 200, 400, phase, p, &bytes,
+ &delays[6], level)) ||
+ !(p =
+ read_output_line (-1, -1, 7, 7, 'L', "DEBUG", -1, 1, phase, p, &bytes,
+ &delay, level)) ||
+ !(p =
+ read_output_line (0, 3, 4, 9, '2', "DEBUG", 200, 400, phase, p, &bytes,
+ &delays[7], level)))
+ {
+ if (bytes == 20 * 16)
+ FPRINTF (stderr, "%s", "Ran out of buffer space!\n");
+ GNUNET_break (0);
+ ok = 2;
+ GNUNET_SCHEDULER_cancel (die_task);
+ GNUNET_SCHEDULER_add_now (&end_task, NULL);
+ return;
+ }
+
+ GNUNET_SCHEDULER_cancel (die_task);
+ GNUNET_SCHEDULER_add_now (&end_task, NULL);
+}
+
+static void
+runone ()
+{
+ const struct GNUNET_DISK_FileHandle *stdout_read_handle;
+
+ pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
+
+ if (pipe_stdout == NULL)
+ {
+ GNUNET_break (0);
+ ok = 2;
+ return;
+ }
+
+ putenv ("GNUNET_LOG=");
+ putenv ("GNUNET_FORCE_LOG=");
+ putenv ("GNUNET_FORCE_LOGFILE=");
+ switch (phase)
+ {
+ case 0:
+ putenv ("GNUNET_LOG=;;;;ERROR");
+ break;
+ case 1:
+ putenv ("GNUNET_LOG=;;;;WARNING");
+ break;
+ case 2:
+ putenv ("GNUNET_LOG=;;;;INFO");
+ break;
+ case 3:
+ putenv ("GNUNET_LOG=;;;;DEBUG");
+ break;
+ case 4:
+ putenv ("GNUNET_FORCE_LOG=;;;;ERROR");
+ break;
+ case 5:
+ putenv ("GNUNET_FORCE_LOG=;;;;WARNING");
+ break;
+ case 6:
+ putenv ("GNUNET_FORCE_LOG=;;;;INFO");
+ break;
+ case 7:
+ putenv ("GNUNET_FORCE_LOG=;;;;DEBUG");
+ break;
+ case 8:
+ putenv ("GNUNET_LOG=blah;;;;ERROR");
+ break;
+ case 9:
+ putenv ("GNUNET_FORCE_LOG=blah;;;;ERROR");
+ break;
+ }
+
+ proc = GNUNET_OS_start_process (GNUNET_NO, NULL, pipe_stdout,
+#if MINGW
+ "test_common_logging_dummy",
+#else
+ "./test_common_logging_dummy",
+#endif
+ "test_common_logging_dummy", NULL);
+ putenv ("GNUNET_FORCE_LOG=");
+ putenv ("GNUNET_LOG=");
+
+ /* Close the write end of the read pipe */
+ GNUNET_DISK_pipe_close_end (pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
+
+ stdout_read_handle =
+ GNUNET_DISK_pipe_handle (pipe_stdout, GNUNET_DISK_PIPE_END_READ);
+
+ die_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 10), &end_task,
+ NULL);
+
+ bytes = 0;
+ buf_ptr = buf;
+ memset (&buf, 0, sizeof (buf));
+
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ stdout_read_handle, &read_call,
+ (void *) stdout_read_handle);
+}
+
+static void
+task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ phase = 0;
+ runone ();
+}
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+check ()
+{
+ ok = 1;
+ GNUNET_SCHEDULER_run (&task, &ok);
+ return ok;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret;
+
+ GNUNET_log_setup ("test-common-logging-runtime-loglevels",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret = check ();
+
+ return ret;
+}
+
+/* end of test_common_logging_runtime_loglevels.c */
diff --git a/src/util/test_configuration.c b/src/util/test_configuration.c
new file mode 100644
index 0000000..b1a446f
--- /dev/null
+++ b/src/util/test_configuration.c
@@ -0,0 +1,553 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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"
+#include "gnunet_disk_lib.h"
+
+#define DEBUG GNUNET_EXTRA_LOGGING
+
+/* Test Configuration Diffs Options */
+enum
+{
+ EDIT_NOTHING,
+ EDIT_SECTION,
+ EDIT_ALL,
+ ADD_NEW_SECTION,
+ ADD_NEW_ENTRY,
+ REMOVE_SECTION,
+ REMOVE_ENTRY,
+ COMPARE
+#if DEBUG
+ , PRINT
+#endif
+};
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+static struct GNUNET_CONFIGURATION_Handle *cfgDefault;
+
+struct DiffsCBData
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct GNUNET_CONFIGURATION_Handle *cfgDiffs;
+ const char *section;
+ int callBackOption;
+ int status;
+};
+
+
+static void
+initDiffsCBData (struct DiffsCBData *cbData)
+{
+ cbData->section = NULL;
+ cbData->cfg = NULL;
+ cbData->cfgDiffs = NULL;
+ cbData->callBackOption = -1;
+ cbData->status = 0;
+}
+
+
+/**
+ * callback function for modifying
+ * and comparing configuration
+*/
+static void
+diffsCallBack (void *cls, const char *section, const char *option,
+ const char *value)
+{
+ struct DiffsCBData *cbData = cls;
+ int cbOption = cbData->callBackOption;
+
+ switch (cbOption)
+ {
+ case EDIT_SECTION:
+ if (NULL == cbData->section)
+ cbData->section = section;
+ if (strcmp (cbData->section, section) == 0)
+ {
+ GNUNET_CONFIGURATION_set_value_string (cbData->cfg, section, option,
+ "new-value");
+ GNUNET_CONFIGURATION_set_value_string (cbData->cfgDiffs, section, option,
+ "new-value");
+ }
+ break;
+ case EDIT_ALL:
+ GNUNET_CONFIGURATION_set_value_string (cbData->cfg, section, option,
+ "new-value");
+ GNUNET_CONFIGURATION_set_value_string (cbData->cfgDiffs, section, option,
+ "new-value");
+ break;
+ case ADD_NEW_ENTRY:
+ {
+ static int hit = 0;
+
+ if (hit == 0)
+ {
+ hit = 1;
+ GNUNET_CONFIGURATION_set_value_string (cbData->cfg, section, "new-key",
+ "new-value");
+ GNUNET_CONFIGURATION_set_value_string (cbData->cfgDiffs, section,
+ "new-key", "new-value");
+ }
+ break;
+ }
+ case COMPARE:
+ {
+ int ret;
+ char *diffValue;
+
+ diffValue = NULL;
+ ret =
+ GNUNET_CONFIGURATION_get_value_string (cbData->cfgDiffs, section,
+ option, &diffValue);
+ if (NULL != diffValue)
+ {
+ if (ret == GNUNET_SYSERR || strcmp (diffValue, value) != 0)
+ cbData->status = 1;
+ }
+ else
+ cbData->status = 1;
+ GNUNET_free_non_null (diffValue);
+ break;
+ }
+#if 0
+ case PRINT:
+ if (NULL == cbData->section)
+ {
+ cbData->section = section;
+ printf ("\nSection: %s\n", section);
+ }
+ else if (strcmp (cbData->section, section) != 0)
+ {
+ cbData->section = section;
+ printf ("\nSection: %s\n", section);
+ }
+ printf ("%s = %s\n", option, value);
+#endif
+ default:
+ break;
+ }
+}
+
+
+static struct GNUNET_CONFIGURATION_Handle *
+editConfiguration (struct GNUNET_CONFIGURATION_Handle *cfg, int option)
+{
+ struct DiffsCBData diffsCB;
+
+ initDiffsCBData (&diffsCB);
+ diffsCB.cfgDiffs = GNUNET_CONFIGURATION_create ();
+
+ switch (option)
+ {
+ case EDIT_SECTION:
+ case EDIT_ALL:
+ case ADD_NEW_ENTRY:
+ diffsCB.callBackOption = option;
+ diffsCB.cfg = cfg;
+ GNUNET_CONFIGURATION_iterate (cfg, diffsCallBack, &diffsCB);
+ break;
+ case EDIT_NOTHING:
+ /* Do nothing */
+ break;
+ case ADD_NEW_SECTION:
+ {
+ int i;
+ char *key;
+
+ for (i = 0; i < 5; i++)
+ {
+ GNUNET_asprintf (&key, "key%d", i);
+ GNUNET_CONFIGURATION_set_value_string (cfg, "new-section", key,
+ "new-value");
+ GNUNET_CONFIGURATION_set_value_string (diffsCB.cfgDiffs, "new-section",
+ key, "new-value");
+ GNUNET_free (key);
+ }
+ break;
+ }
+ case REMOVE_SECTION:
+ break;
+ case REMOVE_ENTRY:
+ break;
+ default:
+ break;
+ }
+
+ return diffsCB.cfgDiffs;
+}
+
+/**
+ * Checking configuration diffs
+ */
+static int
+checkDiffs (struct GNUNET_CONFIGURATION_Handle *cfgDefault, int option)
+{
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ struct GNUNET_CONFIGURATION_Handle *cfgDiffs;
+ struct DiffsCBData cbData;
+ int ret;
+ char *diffsFileName;
+
+ initDiffsCBData (&cbData);
+
+ cfg = GNUNET_CONFIGURATION_create ();
+ /* load defaults */
+ GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_load (cfg, NULL));
+
+ /* Modify configuration and save it */
+ cfgDiffs = editConfiguration (cfg, option);
+ diffsFileName = GNUNET_DISK_mktemp ("gnunet-test-configurations-diffs.conf");
+ if (diffsFileName == NULL)
+ {
+ GNUNET_break (0);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_CONFIGURATION_destroy (cfgDiffs);
+ return 1;
+ }
+ GNUNET_CONFIGURATION_write_diffs (cfgDefault, cfg, diffsFileName);
+ GNUNET_CONFIGURATION_destroy (cfg);
+
+ /* Compare the dumped configuration with modifications done */
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_assert (GNUNET_OK == GNUNET_CONFIGURATION_parse (cfg, diffsFileName));
+ remove (diffsFileName);
+ cbData.callBackOption = COMPARE;
+ cbData.cfgDiffs = cfgDiffs;
+ GNUNET_CONFIGURATION_iterate (cfg, diffsCallBack, &cbData);
+ if (1 == (ret = cbData.status))
+ {
+ FPRINTF (stderr, "%s",
+ "Incorrect Configuration Diffs: Diffs may contain data not actually edited\n");
+ goto housekeeping;
+ }
+ cbData.cfgDiffs = cfg;
+ GNUNET_CONFIGURATION_iterate (cfgDiffs, diffsCallBack, &cbData);
+ if ((ret = cbData.status) == 1)
+ FPRINTF (stderr, "%s",
+ "Incorrect Configuration Diffs: Data may be missing in diffs\n");
+
+housekeeping:
+#if 0
+ cbData.section = NULL;
+ cbData.callBackOption = PRINT;
+ printf ("\nExpected Diffs:\n");
+ GNUNET_CONFIGURATION_iterate (cfgDiffs, diffsCallBack, &cbData);
+ cbData.section = NULL;
+ printf ("\nActual Diffs:\n");
+ GNUNET_CONFIGURATION_iterate (cfg, diffsCallBack, &cbData);
+#endif
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_CONFIGURATION_destroy (cfgDiffs);
+ GNUNET_free (diffsFileName);
+ return ret;
+}
+
+
+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))
+ {
+ GNUNET_break (0);
+ return 3;
+ }
+ if (5 != l)
+ {
+ GNUNET_break (0);
+ return 4;
+ }
+ GNUNET_CONFIGURATION_set_value_string (cfg, "more", "c", "YES");
+ if (GNUNET_NO == GNUNET_CONFIGURATION_get_value_yesno (cfg, "more", "c"))
+ {
+ GNUNET_break (0);
+ return 5;
+ }
+ GNUNET_CONFIGURATION_set_value_number (cfg, "NUMBERS", "TEN", 10);
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg, "NUMBERS", "TEN", &c))
+ {
+ GNUNET_break (0);
+ return 6;
+ }
+ if (0 != strcmp (c, "10"))
+ {
+ GNUNET_free (c);
+ GNUNET_break (0);
+ return 7;
+ }
+ GNUNET_free (c);
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_filename (cfg, "last", "test", &c))
+ {
+ GNUNET_break (0);
+ return 8;
+ }
+#ifndef MINGW
+ if (0 != strcmp (c, "/hello/world"))
+#else
+#define HI "\\hello\\world"
+ if (strstr (c, HI) != c + strlen (c) - strlen (HI))
+#endif
+ {
+ GNUNET_break (0);
+ GNUNET_free (c);
+ return 9;
+ }
+ GNUNET_free (c);
+
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_size (cfg, "last", "size", &l))
+ {
+ GNUNET_break (0);
+ return 10;
+ }
+ if (l != 512 * 1024)
+ {
+ GNUNET_break (0);
+ return 11;
+ }
+ 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;
+ }
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+}
+
+static int
+testConfigFilenames ()
+{
+ int idx;
+
+ idx = 0;
+ if (3 !=
+ GNUNET_CONFIGURATION_iterate_value_filenames (cfg, "FILENAMES", "test",
+ &check, &idx))
+ {
+ GNUNET_break (0);
+ return 8;
+ }
+ if (idx != 3)
+ return 16;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_remove_value_filename (cfg, "FILENAMES", "test",
+ "/File Name"))
+ {
+ GNUNET_break (0);
+ return 24;
+ }
+
+ if (GNUNET_NO !=
+ GNUNET_CONFIGURATION_remove_value_filename (cfg, "FILENAMES", "test",
+ "/File Name"))
+ {
+ GNUNET_break (0);
+ return 32;
+ }
+ if (GNUNET_NO !=
+ GNUNET_CONFIGURATION_remove_value_filename (cfg, "FILENAMES", "test",
+ "Stuff"))
+ {
+ GNUNET_break (0);
+ return 40;
+ }
+
+ if (GNUNET_NO !=
+ GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
+ "/Hello"))
+ {
+ GNUNET_break (0);
+ return 48;
+ }
+ if (GNUNET_NO !=
+ GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
+ "/World"))
+ {
+ GNUNET_break (0);
+ return 56;
+ }
+
+ if (GNUNET_YES !=
+ GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
+ "/File 1"))
+ {
+ GNUNET_break (0);
+ return 64;
+ }
+
+ if (GNUNET_YES !=
+ GNUNET_CONFIGURATION_append_value_filename (cfg, "FILENAMES", "test",
+ "/File 2"))
+ {
+ GNUNET_break (0);
+ 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))
+ {
+ GNUNET_break (0);
+ return 80;
+ }
+ if (idx != 4)
+ {
+ GNUNET_break (0);
+ 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, "%s", "Failed to parse configuration file\n");
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return 1;
+ }
+ failureCount += testConfig ();
+ if (failureCount > 0)
+ goto error;
+
+ failureCount = testConfigFilenames ();
+ if (failureCount > 0)
+ goto error;
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_write (cfg, "/tmp/gnunet-test.conf"))
+ {
+ FPRINTF (stderr, "%s", "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))
+ {
+ GNUNET_break (0);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return 1;
+ }
+ if (0 != strcmp (c, "YES"))
+ {
+ GNUNET_break (0);
+ GNUNET_free (c);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return 1;
+ }
+
+ GNUNET_free (c);
+ GNUNET_CONFIGURATION_destroy (cfg);
+
+ /* Testing configuration diffs */
+ cfgDefault = GNUNET_CONFIGURATION_create ();
+ if (GNUNET_OK != GNUNET_CONFIGURATION_load (cfgDefault, NULL))
+ {
+ GNUNET_break (0);
+ GNUNET_CONFIGURATION_destroy (cfgDefault);
+ return 1;
+ }
+
+ /* Nothing changed in the new configuration */
+ failureCount += checkDiffs (cfgDefault, EDIT_NOTHING);
+
+ /* Modify all entries of the last section */
+ failureCount += checkDiffs (cfgDefault, EDIT_SECTION);
+
+ /* Add a new section */
+ failureCount += checkDiffs (cfgDefault, ADD_NEW_SECTION);
+
+ /* Add a new entry to the last section */
+ failureCount += checkDiffs (cfgDefault, ADD_NEW_ENTRY);
+
+ /* Modify all entries in the configuration */
+ failureCount += checkDiffs (cfgDefault, EDIT_ALL);
+
+ GNUNET_CONFIGURATION_destroy (cfgDefault);
+
+error:
+ 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 0000000..517cbf0
--- /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
+size = 512 KiB
+
+[FILENAMES]
+test = "/Hello /File\ Name /World"
+
+
+[TESTING]
+WEAKRANDOM = YES
+
diff --git a/src/util/test_connection.c b/src/util/test_connection.c
new file mode 100644
index 0000000..cb69f40
--- /dev/null
+++ b/src/util/test_connection.c
@@ -0,0 +1,208 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_connection.c
+ * @brief tests for connection.c
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+
+static struct GNUNET_CONNECTION_Handle *csock;
+
+static struct GNUNET_CONNECTION_Handle *asock;
+
+static struct GNUNET_CONNECTION_Handle *lsock;
+
+static size_t sofar;
+
+static struct GNUNET_NETWORK_Handle *ls;
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+/**
+ * Create and initialize a listen socket for the server.
+ *
+ * @return -1 on error, otherwise the listen socket
+ */
+static struct GNUNET_NETWORK_Handle *
+open_listen_socket ()
+{
+ const static int on = 1;
+ struct sockaddr_in sa;
+ struct GNUNET_NETWORK_Handle *desc;
+
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_port = htons (PORT);
+ sa.sin_family = AF_INET;
+ desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
+ GNUNET_assert (desc != NULL);
+ if (GNUNET_NETWORK_socket_setsockopt
+ (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt");
+ GNUNET_assert (GNUNET_NETWORK_socket_bind
+ (desc, (const struct sockaddr *) &sa,
+ sizeof (sa)) == GNUNET_OK);
+ GNUNET_NETWORK_socket_listen (desc, 5);
+ return desc;
+}
+
+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_CONNECTION_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_CONNECTION_destroy (asock, GNUNET_YES);
+ }
+}
+
+
+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_CONNECTION_create_from_accept (NULL, NULL, ls);
+ GNUNET_assert (asock != NULL);
+ GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock));
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys listen socket\n");
+#endif
+ GNUNET_CONNECTION_destroy (lsock, GNUNET_YES);
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Test asks to receive on accepted socket\n");
+#endif
+ GNUNET_CONNECTION_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");
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test destroys client socket\n");
+#endif
+ GNUNET_CONNECTION_destroy (csock, GNUNET_YES);
+ return 12;
+}
+
+static void
+task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ ls = open_listen_socket ();
+ lsock = GNUNET_CONNECTION_create_from_existing (ls);
+ GNUNET_assert (lsock != NULL);
+ csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT);
+ GNUNET_assert (csock != NULL);
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test asks for write notification\n");
+#endif
+ GNUNET_assert (NULL !=
+ GNUNET_CONNECTION_notify_transmit_ready (csock, 12,
+ GNUNET_TIME_UNIT_SECONDS,
+ &make_hello, NULL));
+#if VERBOSE
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Test prepares to accept\n");
+#endif
+ GNUNET_SCHEDULER_add_read_net (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;
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ GNUNET_SCHEDULER_run (&task, &ok);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return ok;
+}
+
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ GNUNET_log_setup ("test_connection",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check ();
+ return ret;
+}
+
+/* end of test_connection.c */
diff --git a/src/util/test_connection_addressing.c b/src/util/test_connection_addressing.c
new file mode 100644
index 0000000..2d08acc
--- /dev/null
+++ b/src/util/test_connection_addressing.c
@@ -0,0 +1,208 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_connection_addressing.c
+ * @brief tests for connection.c
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+
+static struct GNUNET_CONNECTION_Handle *csock;
+
+static struct GNUNET_CONNECTION_Handle *asock;
+
+static struct GNUNET_CONNECTION_Handle *lsock;
+
+static size_t sofar;
+
+static struct GNUNET_NETWORK_Handle *ls;
+
+
+
+/**
+ * Create and initialize a listen socket for the server.
+ *
+ * @return NULL on error, otherwise the listen socket
+ */
+static struct GNUNET_NETWORK_Handle *
+open_listen_socket ()
+{
+ const static int on = 1;
+ struct sockaddr_in sa;
+ struct GNUNET_NETWORK_Handle *desc;
+
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
+ GNUNET_assert (desc != 0);
+ if (GNUNET_NETWORK_socket_setsockopt
+ (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt");
+ if (GNUNET_OK !=
+ GNUNET_NETWORK_socket_bind (desc, (const struct sockaddr *) &sa,
+ sizeof (sa)))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "bind");
+ GNUNET_assert (0);
+ }
+ GNUNET_NETWORK_socket_listen (desc, 5);
+ return desc;
+}
+
+
+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_CONNECTION_receive (asock, 1024,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, 5), &receive_check,
+ cls);
+ }
+ else
+ {
+ *ok = 0;
+ GNUNET_CONNECTION_destroy (asock, GNUNET_YES);
+ }
+}
+
+
+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_CONNECTION_create_from_accept (NULL, NULL, ls);
+ GNUNET_assert (asock != NULL);
+ GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONNECTION_get_address (asock, &addr, &alen));
+ GNUNET_assert (alen == sizeof (struct sockaddr_in));
+ v4 = addr;
+ memset (&expect, 0, sizeof (expect));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ expect.sin_len = sizeof (expect);
+#endif
+ 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_free (addr);
+ GNUNET_CONNECTION_destroy (lsock, GNUNET_YES);
+ GNUNET_CONNECTION_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_CONNECTION_create_from_existing (ls);
+ GNUNET_assert (lsock != NULL);
+
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ v4.sin_len = sizeof (v4);
+#endif
+ v4.sin_family = AF_INET;
+ v4.sin_port = htons (PORT);
+ v4.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ csock =
+ GNUNET_CONNECTION_create_from_sockaddr (AF_INET,
+ (const struct sockaddr *) &v4,
+ sizeof (v4));
+ GNUNET_assert (csock != NULL);
+ GNUNET_assert (NULL !=
+ GNUNET_CONNECTION_notify_transmit_ready (csock, 12,
+ GNUNET_TIME_UNIT_SECONDS,
+ &make_hello, NULL));
+ GNUNET_CONNECTION_destroy (csock, GNUNET_YES);
+ GNUNET_SCHEDULER_add_read_net (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_connection_addressing",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check ();
+ return ret;
+}
+
+/* end of test_connection_addressing.c */
diff --git a/src/util/test_connection_receive_cancel.c b/src/util/test_connection_receive_cancel.c
new file mode 100644
index 0000000..aa16724
--- /dev/null
+++ b/src/util/test_connection_receive_cancel.c
@@ -0,0 +1,158 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_connection_receive_cancel.c
+ * @brief tests for connection.c
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+
+static struct GNUNET_CONNECTION_Handle *csock;
+
+static struct GNUNET_CONNECTION_Handle *asock;
+
+static struct GNUNET_CONNECTION_Handle *lsock;
+
+static struct GNUNET_NETWORK_Handle *ls;
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+
+/**
+ * Create and initialize a listen socket for the server.
+ *
+ * @return NULL on error, otherwise the listen socket
+ */
+static struct GNUNET_NETWORK_Handle *
+open_listen_socket ()
+{
+ const static int on = 1;
+ struct sockaddr_in sa;
+ struct GNUNET_NETWORK_Handle *desc;
+
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
+ GNUNET_assert (desc != NULL);
+ if (GNUNET_NETWORK_socket_setsockopt
+ (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt");
+ GNUNET_assert (GNUNET_NETWORK_socket_bind
+ (desc, (const struct sockaddr *) &sa,
+ sizeof (sa)) == GNUNET_OK);
+ GNUNET_NETWORK_socket_listen (desc, 5);
+ return desc;
+}
+
+
+
+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_CONNECTION_create_from_accept (NULL, NULL, ls);
+ GNUNET_assert (asock != NULL);
+ GNUNET_assert (GNUNET_YES == GNUNET_CONNECTION_check (asock));
+ GNUNET_CONNECTION_destroy (lsock, GNUNET_YES);
+ GNUNET_CONNECTION_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_CONNECTION_receive_cancel (asock);
+ GNUNET_CONNECTION_destroy (csock, GNUNET_YES);
+ GNUNET_CONNECTION_destroy (asock, GNUNET_YES);
+ *ok = 0;
+}
+
+
+
+static void
+task_receive_cancel (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ ls = open_listen_socket ();
+ lsock = GNUNET_CONNECTION_create_from_existing (ls);
+ GNUNET_assert (lsock != NULL);
+ csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT);
+ GNUNET_assert (csock != NULL);
+ GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, ls,
+ &run_accept_cancel, cls);
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &receive_cancel_task,
+ cls);
+}
+
+
+
+/**
+ * Main method, starts scheduler with task_timeout.
+ */
+static int
+check_receive_cancel ()
+{
+ int ok;
+
+ ok = 1;
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ GNUNET_SCHEDULER_run (&task_receive_cancel, &ok);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return ok;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ GNUNET_log_setup ("test_connection_receive_cancel", "WARNING", NULL);
+ ret += check_receive_cancel ();
+
+ return ret;
+}
+
+/* end of test_connection_receive_cancel.c */
diff --git a/src/util/test_connection_timeout.c b/src/util/test_connection_timeout.c
new file mode 100644
index 0000000..2338665
--- /dev/null
+++ b/src/util/test_connection_timeout.c
@@ -0,0 +1,154 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_connection_timeout.c
+ * @brief tests for connection.c
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+static struct GNUNET_CONNECTION_Handle *csock;
+
+static struct GNUNET_CONNECTION_Handle *lsock;
+
+static struct GNUNET_NETWORK_Handle *ls;
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+
+/**
+ * Create and initialize a listen socket for the server.
+ *
+ * @return NULL on error, otherwise the listen socket
+ */
+static struct GNUNET_NETWORK_Handle *
+open_listen_socket ()
+{
+ const static int on = 1;
+ struct sockaddr_in sa;
+ struct GNUNET_NETWORK_Handle *desc;
+
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ desc = GNUNET_NETWORK_socket_create (AF_INET, SOCK_STREAM, 0);
+ GNUNET_assert (desc != NULL);
+ if (GNUNET_NETWORK_socket_setsockopt
+ (desc, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK)
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, "setsockopt");
+ GNUNET_assert (GNUNET_NETWORK_socket_bind
+ (desc, (const struct sockaddr *) &sa,
+ sizeof (sa)) == GNUNET_OK);
+ GNUNET_NETWORK_socket_listen (desc, 5);
+ return desc;
+}
+
+
+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_CONNECTION_destroy (lsock, GNUNET_YES);
+ GNUNET_CONNECTION_destroy (csock, GNUNET_YES);
+ 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_CONNECTION_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_CONNECTION_create_from_existing (ls);
+ GNUNET_assert (lsock != NULL);
+ csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT);
+ GNUNET_assert (csock != NULL);
+ GNUNET_assert (NULL !=
+ GNUNET_CONNECTION_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;
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ GNUNET_SCHEDULER_run (&task_timeout, &ok);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return ok;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ GNUNET_log_setup ("test_connection_timeout",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check_timeout ();
+ return ret;
+}
+
+/* end of test_connection_timeout.c */
diff --git a/src/util/test_connection_timeout_no_connect.c b/src/util/test_connection_timeout_no_connect.c
new file mode 100644
index 0000000..2e8f9be
--- /dev/null
+++ b/src/util/test_connection_timeout_no_connect.c
@@ -0,0 +1,101 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_connection_timeout_no_connect.c
+ * @brief tests for connection.c, doing timeout which connect failure
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 13425
+
+static struct GNUNET_CONNECTION_Handle *csock;
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+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_CONNECTION_create_from_connect (cfg, "localhost", PORT);
+ GNUNET_assert (csock != NULL);
+ GNUNET_assert (NULL !=
+ GNUNET_CONNECTION_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;
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ GNUNET_SCHEDULER_run (&task_timeout, &ok);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return ok;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ GNUNET_log_setup ("test_connection_timeout_no_connect",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check_timeout ();
+ return ret;
+}
+
+/* end of test_connection_timeout_no_connect.c */
diff --git a/src/util/test_connection_transmit_cancel.c b/src/util/test_connection_transmit_cancel.c
new file mode 100644
index 0000000..d81c32a
--- /dev/null
+++ b/src/util/test_connection_transmit_cancel.c
@@ -0,0 +1,101 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_connection_transmit_cancel.c
+ * @brief tests for connection.c
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+
+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_CONNECTION_TransmitHandle *th;
+ struct GNUNET_CONNECTION_Handle *csock;
+
+ csock = GNUNET_CONNECTION_create_from_connect (cfg, "localhost", PORT);
+ GNUNET_assert (csock != NULL);
+ th = GNUNET_CONNECTION_notify_transmit_ready (csock, 12,
+ GNUNET_TIME_UNIT_MINUTES,
+ &not_run, cls);
+ GNUNET_assert (NULL != th);
+ GNUNET_CONNECTION_notify_transmit_ready_cancel (th);
+ GNUNET_CONNECTION_destroy (csock, GNUNET_YES);
+ *ok = 0;
+}
+
+
+
+
+/**
+ * Main method, starts scheduler with task_timeout.
+ */
+static int
+check_transmit_cancel ()
+{
+ int ok;
+
+ ok = 1;
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ GNUNET_SCHEDULER_run (&task_transmit_cancel, &ok);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ return ok;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ GNUNET_log_setup ("test_connection_transmit_cancel",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check_transmit_cancel ();
+
+ return ret;
+}
+
+/* end of test_connection_transmit_cancel.c */
diff --git a/src/util/test_container_bloomfilter.c b/src/util/test_container_bloomfilter.c
new file mode 100644
index 0000000..f881bb3
--- /dev/null
+++ b/src/util/test_container_bloomfilter.c
@@ -0,0 +1,244 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 (GNUNET_CRYPTO_QUALITY_WEAK, hc);
+}
+
+static int
+add_iterator (void *cls, GNUNET_HashCode * next)
+{
+ int *ret = cls;
+ 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);
+ GNUNET_CRYPTO_seed_weak_random (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);
+ }
+ GNUNET_CRYPTO_seed_weak_random (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);
+
+ GNUNET_CRYPTO_seed_weak_random (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;
+ }
+
+ GNUNET_CRYPTO_seed_weak_random (1);
+ for (i = 0; i < 100; i++)
+ {
+ nextHC (&tmp);
+ GNUNET_CONTAINER_bloomfilter_remove (bf, &tmp);
+ GNUNET_CONTAINER_bloomfilter_remove (bfi, &tmp);
+ }
+
+ GNUNET_CRYPTO_seed_weak_random (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;
+ }
+
+ GNUNET_CRYPTO_seed_weak_random (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;
+ }
+
+ GNUNET_CRYPTO_seed_weak_random (2);
+ i = 20;
+ GNUNET_CONTAINER_bloomfilter_resize (bfi, &add_iterator, &i, SIZE * 2, K);
+
+ GNUNET_CRYPTO_seed_weak_random (2);
+ i = 20;
+ GNUNET_CONTAINER_bloomfilter_resize (bf, &add_iterator, &i, SIZE * 2, K);
+ GNUNET_CRYPTO_seed_weak_random (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 0000000..a2a004a
--- /dev/null
+++ b/src/util/test_container_heap.c
@@ -0,0 +1,290 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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/test_container_heap.c
+ * @brief Test of heap operations
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_container_lib.h"
+
+static int
+iterator_callback (void *cls, struct GNUNET_CONTAINER_HeapNode *node,
+ void *element, GNUNET_CONTAINER_HeapCostType cost)
+{
+ return GNUNET_OK;
+}
+
+static int
+nstrcmp (const char *a, const char *b)
+{
+ GNUNET_assert (a != NULL);
+ GNUNET_assert (b != NULL);
+ return strcmp (a, b);
+}
+
+static int
+check ()
+{
+ struct GNUNET_CONTAINER_Heap *myHeap;
+ struct GNUNET_CONTAINER_HeapNode *n1;
+ struct GNUNET_CONTAINER_HeapNode *n2;
+ struct GNUNET_CONTAINER_HeapNode *n3;
+ struct GNUNET_CONTAINER_HeapNode *n4;
+ struct GNUNET_CONTAINER_HeapNode *n5;
+ struct GNUNET_CONTAINER_HeapNode *n6;
+ struct GNUNET_CONTAINER_HeapNode *n7;
+ struct GNUNET_CONTAINER_HeapNode *n8;
+ const char *r;
+
+ myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+
+ // GNUNET_CONTAINER_heap_remove_root heap empty, taking if-branch
+ n1 = GNUNET_CONTAINER_heap_remove_root (myHeap);
+ GNUNET_assert (NULL == n1);
+
+ // GNUNET_CONTAINER_heap_peek heap empty, taking if-branch
+ n1 = GNUNET_CONTAINER_heap_peek (myHeap);
+ GNUNET_assert (NULL == n1);
+
+ // GNUNET_CONTAINER_heap_walk_get_next: heap empty, taking if-branch
+ n1 = GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_assert (NULL == n1);
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "11", 11);
+ GNUNET_assert (NULL != n1);
+
+
+ // GNUNET_CONTAINER_heap_peek not empty, taking if-branch
+ n2 = NULL;
+ n2 = GNUNET_CONTAINER_heap_peek (myHeap);
+ GNUNET_assert (NULL != n2);
+
+ // GNUNET_CONTAINER_heap_walk_get_next: 1 element
+ n1 = NULL;
+ n1 = GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_assert (NULL != n1);
+
+ GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
+ GNUNET_assert (1 == GNUNET_CONTAINER_heap_get_size (myHeap));
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "78", 78);
+ GNUNET_assert (2 == GNUNET_CONTAINER_heap_get_size (myHeap));
+ GNUNET_assert (0 == strcmp ("78", GNUNET_CONTAINER_heap_remove_node (n2)));
+ GNUNET_assert (1 == GNUNET_CONTAINER_heap_get_size (myHeap));
+ GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
+
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "15", 5);
+ GNUNET_CONTAINER_heap_update_cost (myHeap, n3, 15);
+ GNUNET_assert (2 == GNUNET_CONTAINER_heap_get_size (myHeap));
+ GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
+
+ n4 = GNUNET_CONTAINER_heap_insert (myHeap, "50", 50);
+ GNUNET_CONTAINER_heap_update_cost (myHeap, n4, 50);
+ GNUNET_assert (3 == GNUNET_CONTAINER_heap_get_size (myHeap));
+ GNUNET_CONTAINER_heap_iterate (myHeap, &iterator_callback, NULL);
+
+ n5 = GNUNET_CONTAINER_heap_insert (myHeap, "100", 100);
+ n6 = GNUNET_CONTAINER_heap_insert (myHeap, "30/200", 30);
+ GNUNET_assert (5 == GNUNET_CONTAINER_heap_get_size (myHeap));
+ GNUNET_CONTAINER_heap_remove_node (n5);
+ r = GNUNET_CONTAINER_heap_remove_root (myHeap); /* n1 */
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("11", r));
+ GNUNET_CONTAINER_heap_update_cost (myHeap, n6, 200);
+ GNUNET_CONTAINER_heap_remove_node (n3);
+ r = GNUNET_CONTAINER_heap_remove_root (myHeap); /* n4 */
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("50", r));
+ r = GNUNET_CONTAINER_heap_remove_root (myHeap); /* n6 */
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("30/200", r));
+ GNUNET_assert (0 == GNUNET_CONTAINER_heap_get_size (myHeap));
+
+ GNUNET_CONTAINER_heap_destroy (myHeap);
+
+ // My additions to a complete testcase
+ // Testing a GNUNET_CONTAINER_HEAP_ORDER_MIN
+ // Testing remove_node
+
+ myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MIN);
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ GNUNET_CONTAINER_heap_update_cost (myHeap, n1, 15);
+
+ r = GNUNET_CONTAINER_heap_remove_node (n1);
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("10", r));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
+
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ r = GNUNET_CONTAINER_heap_remove_node (n2);
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("20", r));
+ r = GNUNET_CONTAINER_heap_remove_node (n1);
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("10", r));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
+
+ GNUNET_CONTAINER_heap_remove_node (n2);
+ GNUNET_CONTAINER_heap_remove_node (n1);
+ r = GNUNET_CONTAINER_heap_remove_root (myHeap);
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("30", r));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
+
+ GNUNET_CONTAINER_heap_remove_node (n2);
+ GNUNET_CONTAINER_heap_remove_node (n1);
+ r = GNUNET_CONTAINER_heap_remove_node (n3);
+ GNUNET_assert (NULL != r);
+ GNUNET_assert (0 == strcmp ("30", r));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 20);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 30);
+
+ GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
+ GNUNET_assert (0 ==
+ nstrcmp ("10", GNUNET_CONTAINER_heap_remove_root (myHeap)));
+ GNUNET_assert (0 ==
+ nstrcmp ("30", GNUNET_CONTAINER_heap_remove_root (myHeap)));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 20);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 30);
+ n4 = GNUNET_CONTAINER_heap_insert (myHeap, "40", 40);
+ n5 = GNUNET_CONTAINER_heap_insert (myHeap, "50", 50);
+ n6 = GNUNET_CONTAINER_heap_insert (myHeap, "60", 60);
+
+ // Inserting nodes deeper in the tree with lower costs
+ n7 = GNUNET_CONTAINER_heap_insert (myHeap, "70", 10);
+ n8 = GNUNET_CONTAINER_heap_insert (myHeap, "80", 10);
+
+ GNUNET_assert (0 == nstrcmp ("30", GNUNET_CONTAINER_heap_remove_node (n3)));
+
+ // Cleaning up...
+ GNUNET_assert (0 == nstrcmp ("60", GNUNET_CONTAINER_heap_remove_node (n6)));
+ GNUNET_assert (0 == nstrcmp ("50", GNUNET_CONTAINER_heap_remove_node (n5)));
+
+ // Testing heap_walk_get_next
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);;
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+
+ GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
+ GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
+ GNUNET_assert (0 == nstrcmp ("40", GNUNET_CONTAINER_heap_remove_node (n4)));
+ GNUNET_assert (0 == nstrcmp ("70", GNUNET_CONTAINER_heap_remove_node (n7)));
+ GNUNET_assert (0 == nstrcmp ("80", GNUNET_CONTAINER_heap_remove_node (n8)));
+
+ // End Testing remove_node
+
+ // Testing a GNUNET_CONTAINER_HEAP_ORDER_MAX
+ GNUNET_CONTAINER_heap_destroy (myHeap);
+
+ myHeap = GNUNET_CONTAINER_heap_create (GNUNET_CONTAINER_HEAP_ORDER_MAX);
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ GNUNET_CONTAINER_heap_update_cost (myHeap, n1, 15);
+
+ GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
+
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
+ GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
+
+ GNUNET_CONTAINER_heap_remove_node (n2);
+ GNUNET_CONTAINER_heap_remove_node (n1);
+ GNUNET_assert (0 ==
+ nstrcmp ("30", GNUNET_CONTAINER_heap_remove_root (myHeap)));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 10);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 10);
+
+ GNUNET_CONTAINER_heap_remove_node (n2);
+ GNUNET_CONTAINER_heap_remove_node (n1);
+ GNUNET_assert (0 == nstrcmp ("30", GNUNET_CONTAINER_heap_remove_node (n3)));
+
+ n1 = GNUNET_CONTAINER_heap_insert (myHeap, "10", 10);
+ n2 = GNUNET_CONTAINER_heap_insert (myHeap, "20", 20);
+ n3 = GNUNET_CONTAINER_heap_insert (myHeap, "30", 30);
+ n4 = GNUNET_CONTAINER_heap_insert (myHeap, "40", 40);
+ n5 = GNUNET_CONTAINER_heap_insert (myHeap, "50", 50);
+ n6 = GNUNET_CONTAINER_heap_insert (myHeap, "60", 60);
+
+ // Inserting nodes deeper in the tree with lower costs
+ n7 = GNUNET_CONTAINER_heap_insert (myHeap, "70", 10);
+ n8 = GNUNET_CONTAINER_heap_insert (myHeap, "80", 10);
+
+ GNUNET_assert (0 == nstrcmp ("30", GNUNET_CONTAINER_heap_remove_node (n3)));
+
+ // Cleaning up...
+ GNUNET_assert (0 == nstrcmp ("60", GNUNET_CONTAINER_heap_remove_node (n6)));
+ GNUNET_assert (0 == nstrcmp ("50", GNUNET_CONTAINER_heap_remove_node (n5)));
+
+ // Testing heap_walk_get_next
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);;
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+ GNUNET_CONTAINER_heap_walk_get_next (myHeap);
+
+ GNUNET_assert (0 == nstrcmp ("10", GNUNET_CONTAINER_heap_remove_node (n1)));
+ GNUNET_assert (0 == nstrcmp ("20", GNUNET_CONTAINER_heap_remove_node (n2)));
+ GNUNET_assert (0 == nstrcmp ("40", GNUNET_CONTAINER_heap_remove_node (n4)));
+ GNUNET_assert (0 == nstrcmp ("70", GNUNET_CONTAINER_heap_remove_node (n7)));
+ GNUNET_assert (0 == nstrcmp ("80", GNUNET_CONTAINER_heap_remove_node (n8)));
+
+ // End Testing remove_node
+
+ GNUNET_CONTAINER_heap_destroy (myHeap);
+
+ return 0;
+}
+
+
+int
+main (int argc, char **argv)
+{
+ GNUNET_log_setup ("test-container-heap", "WARNING", NULL);
+ return check ();
+}
+
+/* end of test_container_heap.c */
diff --git a/src/util/test_container_meta_data.c b/src/util/test_container_meta_data.c
new file mode 100644
index 0000000..fe1dd79
--- /dev/null
+++ b/src/util/test_container_meta_data.c
@@ -0,0 +1,347 @@
+/*
+ This file is part of GNUnet.
+ (C) 2003, 2004, 2006, 2009, 2010 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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[256];
+ char *sval;
+ int j;
+ unsigned int size;
+
+ m = GNUNET_CONTAINER_meta_data_create ();
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_insert (m, "<test>", EXTRACTOR_METATYPE_TITLE,
+ EXTRACTOR_METAFORMAT_UTF8,
+ "text/plain", "TestTitle",
+ strlen ("TestTitle") + 1))
+ ABORT (m);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_insert (m, "<test>",
+ EXTRACTOR_METATYPE_AUTHOR_NAME,
+ EXTRACTOR_METAFORMAT_UTF8,
+ "text/plain", "TestTitle",
+ strlen ("TestTitle") + 1))
+ ABORT (m);
+ if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, "<test>", EXTRACTOR_METATYPE_TITLE, EXTRACTOR_METAFORMAT_UTF8, "text/plain", "TestTitle", strlen ("TestTitle") + 1)) /* dup! */
+ ABORT (m);
+ if (GNUNET_OK == GNUNET_CONTAINER_meta_data_insert (m, "<test>", EXTRACTOR_METATYPE_AUTHOR_NAME, EXTRACTOR_METAFORMAT_UTF8, "text/plain", "TestTitle", strlen ("TestTitle") + 1)) /* dup! */
+ ABORT (m);
+ if (2 != GNUNET_CONTAINER_meta_data_iterate (m, NULL, NULL))
+ ABORT (m);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_METATYPE_AUTHOR_NAME,
+ "TestTitle", strlen ("TestTitle") + 1))
+ ABORT (m);
+ if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_METATYPE_AUTHOR_NAME, "TestTitle", strlen ("TestTitle") + 1)) /* already gone */
+ ABORT (m);
+ if (1 != GNUNET_CONTAINER_meta_data_iterate (m, NULL, NULL))
+ ABORT (m);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_METATYPE_TITLE,
+ "TestTitle", strlen ("TestTitle") + 1))
+ ABORT (m);
+ if (GNUNET_OK == GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_METATYPE_TITLE, "TestTitle", strlen ("TestTitle") + 1)) /* already gone */
+ ABORT (m);
+ if (0 != GNUNET_CONTAINER_meta_data_iterate (m, NULL, NULL))
+ ABORT (m);
+ for (j = 0; j < i; j++)
+ {
+ GNUNET_snprintf (val, sizeof (val), "%s.%d",
+ "A teststring that should compress well.", j);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_insert (m, "<test>",
+ EXTRACTOR_METATYPE_UNKNOWN,
+ EXTRACTOR_METAFORMAT_UTF8,
+ "text/plain", val, strlen (val) + 1))
+ ABORT (m);
+ }
+ if (i != GNUNET_CONTAINER_meta_data_iterate (m, NULL, NULL))
+ ABORT (m);
+
+ size = GNUNET_CONTAINER_meta_data_get_serialized_size (m);
+ sval = NULL;
+ if (size !=
+ GNUNET_CONTAINER_meta_data_serialize (m, &sval, size,
+ GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL))
+ {
+ GNUNET_free_non_null (sval);
+ ABORT (m);
+ }
+ GNUNET_CONTAINER_meta_data_destroy (m);
+ m = GNUNET_CONTAINER_meta_data_deserialize (sval, size);
+ GNUNET_free (sval);
+ if (m == NULL)
+ ABORT (m);
+ for (j = 0; j < i; j++)
+ {
+ GNUNET_snprintf (val, sizeof (val), "%s.%d",
+ "A teststring that should compress well.", j);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_delete (m, EXTRACTOR_METATYPE_UNKNOWN, val,
+ strlen (val) + 1))
+ {
+ ABORT (m);
+ }
+ }
+ if (0 != GNUNET_CONTAINER_meta_data_iterate (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, "<test>",
+ q % EXTRACTOR_metatype_get_max (),
+ EXTRACTOR_METAFORMAT_UTF8, "text/plain",
+ txt, strlen (txt) + 1);
+ }
+ size = GNUNET_CONTAINER_meta_data_get_serialized_size (meta);
+ 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, "<test>",
+ EXTRACTOR_METATYPE_UNKNOWN,
+ EXTRACTOR_METAFORMAT_UTF8,
+ "text/plain", "link",
+ strlen ("link") + 1))
+ ABORT (m);
+ if (GNUNET_OK !=
+ GNUNET_CONTAINER_meta_data_insert (m, "<test>",
+ EXTRACTOR_METATYPE_FILENAME,
+ EXTRACTOR_METAFORMAT_UTF8,
+ "text/plain", "lib-link.m4",
+ strlen ("lib-link.m4") + 1))
+ ABORT (m);
+ val = NULL;
+ size =
+ GNUNET_CONTAINER_meta_data_serialize (m, &val, (size_t) - 1,
+ GNUNET_CONTAINER_META_DATA_SERIALIZE_FULL);
+ 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;
+}
+
+int
+check ()
+{
+ struct GNUNET_CONTAINER_MetaData *meta;
+ struct GNUNET_CONTAINER_MetaData *meta2;
+ int q;
+ int i = 100;
+ char txt[128];
+ char *str;
+ unsigned char *thumb;
+
+ meta = GNUNET_CONTAINER_meta_data_create ();
+ meta2 = 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, "<test>",
+ EXTRACTOR_METATYPE_UNKNOWN,
+ EXTRACTOR_METAFORMAT_UTF8, "text/plain",
+ "TestTitle", strlen ("TestTitle") + 1);
+ GNUNET_CONTAINER_meta_data_insert (meta2, "<test>",
+ EXTRACTOR_METATYPE_UNKNOWN,
+ EXTRACTOR_METAFORMAT_UTF8, "text/plain",
+ "TestTitle", strlen ("TestTitle") + 1);
+ }
+
+ //check meta_data_test_equal
+ if (GNUNET_YES != GNUNET_CONTAINER_meta_data_test_equal (meta, meta2))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+
+ //check meta_data_clear
+ GNUNET_CONTAINER_meta_data_clear (meta2);
+ if (0 != GNUNET_CONTAINER_meta_data_iterate (meta2, NULL, NULL))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+ // check equal branch in meta_data_test_equal
+ if (GNUNET_YES != GNUNET_CONTAINER_meta_data_test_equal (meta, meta))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+ // check "count" branch in meta_data_test_equal
+ if (GNUNET_NO != GNUNET_CONTAINER_meta_data_test_equal (meta, meta2))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+
+ // check meta_data_add_publication_date
+ GNUNET_CONTAINER_meta_data_add_publication_date (meta2);
+
+ // check meta_data_merge
+ GNUNET_CONTAINER_meta_data_clear (meta2);
+ GNUNET_CONTAINER_meta_data_merge (meta2, meta);
+ if (100 == GNUNET_CONTAINER_meta_data_iterate (meta2, NULL, NULL))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+
+ // check meta_data_get_by_type
+ GNUNET_CONTAINER_meta_data_clear (meta2);
+ if (NULL !=
+ (str =
+ GNUNET_CONTAINER_meta_data_get_by_type (meta2,
+ EXTRACTOR_METATYPE_UNKNOWN)))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ GNUNET_free (str);
+ ABORT (meta);
+ }
+
+ str =
+ GNUNET_CONTAINER_meta_data_get_by_type (meta, EXTRACTOR_METATYPE_UNKNOWN);
+ GNUNET_assert (NULL != str);
+ if (str[0] != 'T')
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ GNUNET_free (str);
+ ABORT (meta);
+ }
+ GNUNET_free (str);
+
+ // check branch
+ if (NULL !=
+ (str =
+ GNUNET_CONTAINER_meta_data_get_by_type (meta,
+ EXTRACTOR_METATYPE_PUBLICATION_DATE)))
+ {
+ GNUNET_free (str);
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+
+ //check meta_data_get_first_by_types
+ str =
+ GNUNET_CONTAINER_meta_data_get_first_by_types (meta,
+ EXTRACTOR_METATYPE_UNKNOWN,
+ -1);
+ GNUNET_assert (NULL != str);
+ if (str[0] != 'T')
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ GNUNET_free (str);
+ ABORT (meta);
+ }
+ GNUNET_free (str);
+
+ //check meta_data_get_thumbnail
+ if (GNUNET_CONTAINER_meta_data_get_thumbnail (meta, &thumb) != 0)
+ {
+ GNUNET_free (thumb);
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ //check meta_data_duplicate
+ meta2 = GNUNET_CONTAINER_meta_data_duplicate (meta);
+ if (200 == GNUNET_CONTAINER_meta_data_iterate (meta2, NULL, NULL))
+ {
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ ABORT (meta);
+ }
+ GNUNET_CONTAINER_meta_data_destroy (meta2);
+ GNUNET_CONTAINER_meta_data_destroy (meta);
+ 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 ();
+
+ int ret = check ();
+
+ if (ret == 1)
+ return 1;
+
+ if (failureCount != 0)
+ return 1;
+ return 0;
+}
+
+/* end of test_container_meta_data.c */
diff --git a/src/util/test_container_multihashmap.c b/src/util/test_container_multihashmap.c
new file mode 100644
index 0000000..ba621c1
--- /dev/null
+++ b/src/util/test_container_multihashmap.c
@@ -0,0 +1,105 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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;
+ const char *ret;
+ int j;
+
+ 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));
+ ret = GNUNET_CONTAINER_multihashmap_get (m, &k1);
+ GNUNET_assert (ret != NULL);
+ CHECK (0 == strcmp ("v1", ret));
+ 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));
+ 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 test_container_multihashmap.c */
diff --git a/src/util/test_container_slist.c b/src/util/test_container_slist.c
new file mode 100644
index 0000000..1b63d3d
--- /dev/null
+++ b/src/util/test_container_slist.c
@@ -0,0 +1,160 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_slist.c
+ * @brief Testcases for singly linked lists
+ * @author Nils Durner
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_container_lib.h"
+
+int
+main (int argc, char *argv[])
+{
+ struct GNUNET_CONTAINER_SList *l;
+ struct GNUNET_CONTAINER_SList_Iterator it;
+ unsigned int i;
+ int *ip;
+ unsigned int j;
+ size_t s;
+ const void *p;
+
+ GNUNET_log_setup ("test-container-slist", "WARNING", NULL);
+
+ l = GNUNET_CONTAINER_slist_create ();
+ GNUNET_assert (l != NULL);
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 0);
+
+ for (i = 0; i < 100; i++)
+ GNUNET_CONTAINER_slist_add (l, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ &i, sizeof (i));
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 100);
+
+ for (it = GNUNET_CONTAINER_slist_begin (l), i = 99;
+ GNUNET_CONTAINER_slist_end (&it) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&it), i--)
+ {
+ p = GNUNET_CONTAINER_slist_get (&it, &s);
+
+ if ((p == NULL) || (i != (j = *(int *) p)) || (s != sizeof (i)))
+ {
+ GNUNET_CONTAINER_slist_iter_destroy (&it);
+ GNUNET_assert (0);
+ }
+ j *= 2;
+ GNUNET_CONTAINER_slist_insert (&it,
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ &j, sizeof (j));
+ }
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 200);
+ i = 198;
+ GNUNET_assert (GNUNET_CONTAINER_slist_contains (l, &i, sizeof (i)));
+
+ for (it = GNUNET_CONTAINER_slist_begin (l);
+ GNUNET_CONTAINER_slist_end (&it) != GNUNET_YES;)
+ {
+ p = GNUNET_CONTAINER_slist_get (&it, &s);
+ GNUNET_assert (p != NULL);
+ GNUNET_assert (s == sizeof (i));
+ i = *(int *) p;
+
+ GNUNET_assert (GNUNET_CONTAINER_slist_next (&it) == GNUNET_YES);
+ GNUNET_assert (GNUNET_CONTAINER_slist_end (&it) != GNUNET_YES);
+
+ p = GNUNET_CONTAINER_slist_get (&it, &s);
+ GNUNET_assert (p != NULL);
+ GNUNET_assert (s == sizeof (j));
+ j = *(int *) p;
+
+ GNUNET_assert (j * 2 == i);
+
+ GNUNET_CONTAINER_slist_erase (&it);
+ }
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 100);
+ i = 99;
+ GNUNET_assert (GNUNET_CONTAINER_slist_contains (l, &i, sizeof (i)) ==
+ GNUNET_NO);
+ i = 198;
+ GNUNET_assert (GNUNET_CONTAINER_slist_contains (l, &i, sizeof (i)) ==
+ GNUNET_YES);
+
+ GNUNET_CONTAINER_slist_clear (l);
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 0);
+
+ for (i = 0; i < 100; i++)
+ GNUNET_CONTAINER_slist_add (l, GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ &i, sizeof (i));
+ /*check slist_append */
+ GNUNET_CONTAINER_slist_append (l, l);
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 200);
+
+ GNUNET_CONTAINER_slist_destroy (l);
+
+ /*check slist_add_end */
+ l = GNUNET_CONTAINER_slist_create ();
+ for (i = 0; i < 100; i++)
+ GNUNET_CONTAINER_slist_add_end (l,
+ GNUNET_CONTAINER_SLIST_DISPOSITION_TRANSIENT,
+ &i, sizeof (i));
+
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 100);
+
+ for (it = GNUNET_CONTAINER_slist_begin (l), i = 0;
+ GNUNET_CONTAINER_slist_end (&it) != GNUNET_YES;
+ GNUNET_CONTAINER_slist_next (&it), i++)
+ {
+ p = GNUNET_CONTAINER_slist_get (&it, &s);
+
+ if ((p == NULL) || (i != *(int *) p) || (s != sizeof (i)))
+ {
+ GNUNET_assert (0);
+ }
+ }
+ GNUNET_CONTAINER_slist_destroy (l);
+
+ /*check if disp = GNUNET_CONTAINER_SLIST_DISPOSITION_DYNAMIC */
+ l = GNUNET_CONTAINER_slist_create ();
+
+ for (i = 0; i < 100; i++)
+ {
+ ip = GNUNET_malloc (sizeof (int));
+ *ip = i;
+ GNUNET_CONTAINER_slist_add (l, GNUNET_CONTAINER_SLIST_DISPOSITION_DYNAMIC,
+ ip, sizeof (int));
+ }
+ //creat_add
+ it = GNUNET_CONTAINER_slist_begin (l);
+ p = GNUNET_CONTAINER_slist_get (&it, &s);
+ GNUNET_assert (p != NULL);
+ //slist_erase
+ GNUNET_assert (GNUNET_CONTAINER_slist_next (&it) == GNUNET_YES);
+ GNUNET_CONTAINER_slist_erase (&it);
+ GNUNET_CONTAINER_slist_iter_destroy (&it);
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 99);
+ //slist_clear
+ GNUNET_CONTAINER_slist_clear (l);
+ GNUNET_assert (GNUNET_CONTAINER_slist_count (l) == 0);
+ GNUNET_CONTAINER_slist_destroy (l);
+
+ return 0;
+}
diff --git a/src/util/test_crypto_aes.c b/src/util/test_crypto_aes.c
new file mode 100644
index 0000000..971e9af
--- /dev/null
+++ b/src/util/test_crypto_aes.c
@@ -0,0 +1,175 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 (result, size, &key,
+ (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
+ };
+
+ 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 (result, GNUNET_CRYPTO_AES_KEY_LENGTH, &key,
+ (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 0000000..0b7ba5c
--- /dev/null
+++ b/src/util/test_crypto_aes_weak.c
@@ -0,0 +1,202 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 <gcrypt.h>
+
+#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 (result, size, &weak_key, &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, "%s", ".");
+ /*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));
+ 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 0000000..99eca0c
--- /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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 0000000..bc04114
--- /dev/null
+++ b/src/util/test_crypto_hash.c
@@ -0,0 +1,166 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 (GNUNET_CRYPTO_QUALITY_WEAK, &h1);
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &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_assert (NULL !=
+ GNUNET_CRYPTO_hash_file (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
+ 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_hkdf.c b/src/util/test_crypto_hkdf.c
new file mode 100644
index 0000000..7521161
--- /dev/null
+++ b/src/util/test_crypto_hkdf.c
@@ -0,0 +1,351 @@
+/*
+ Copyright (c) 2010 Nils Durner
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ THE SOFTWARE.
+*/
+
+/**
+ * @file src/util/test_crypt_hkdf.c
+ * @brief Testcases for HKDF
+ * @todo: test for out_len < hash_len
+ * @author Nils Durner
+ */
+
+#include <gcrypt.h>
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+
+void
+tc1 ()
+{
+ unsigned char ikm[22] =
+ { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+ };
+ unsigned char salt[13] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c
+ };
+ unsigned char info[10] =
+ { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
+ unsigned char okm[42] =
+ { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43,
+ 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a,
+ 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, 0x34, 0x00,
+ 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65
+ };
+ unsigned char result[44];
+ int l = 42;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA256, GCRY_MD_SHA256, salt,
+ sizeof (salt), ikm, sizeof (ikm), info, sizeof (info),
+ NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc2 ()
+{
+ unsigned char ikm[80] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
+ 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+ };
+ unsigned char salt[80] =
+ { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+ 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+ };
+ unsigned char info[80] =
+ { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
+ 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+ unsigned char okm[82] =
+ { 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7,
+ 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, 0x2d, 0x4e,
+ 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, 0xa9, 0x7c, 0x59, 0x04,
+ 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59,
+ 0x0e, 0x09, 0xda, 0x32, 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77,
+ 0x93, 0xa9, 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec,
+ 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, 0x1d, 0x87
+ };
+ char result[84];
+ int l = 82;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA256, GCRY_MD_SHA256, salt,
+ sizeof (salt), ikm, sizeof (ikm), info, sizeof (info),
+ NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc3 ()
+{
+ unsigned char ikm[22] =
+ { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+ };
+ unsigned char okm[42] =
+ { 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f,
+ 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1,
+ 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, 0x9d, 0x20,
+ 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8
+ };
+ unsigned char result[44];
+ int l = 42;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA256, GCRY_MD_SHA256, NULL, 0, ikm,
+ sizeof (ikm), NULL, 0, NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc4 ()
+{
+ unsigned char ikm[11] =
+ { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b
+ };
+ unsigned char salt[13] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c
+ };
+ unsigned char info[10] =
+ { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 };
+ unsigned char okm[42] =
+ { 0x08, 0x5a, 0x01, 0xea, 0x1b, 0x10, 0xf3, 0x69, 0x33, 0x06,
+ 0x8b, 0x56, 0xef, 0xa5, 0xad, 0x81, 0xa4, 0xf1, 0x4b, 0x82, 0x2f, 0x5b,
+ 0x09, 0x15, 0x68, 0xa9, 0xcd, 0xd4, 0xf1, 0x55, 0xfd, 0xa2, 0xc2, 0x2e,
+ 0x42, 0x24, 0x78, 0xd3, 0x05, 0xf3, 0xf8, 0x96
+ };
+ char result[84];
+ int l = 42;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, salt, sizeof (salt),
+ ikm, sizeof (ikm), info, sizeof (info), NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc5 ()
+{
+ unsigned char ikm[80] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
+ 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+ };
+ unsigned char salt[80] =
+ { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+ 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+ };
+ unsigned char info[80] =
+ { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,
+ 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5,
+ 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1,
+ 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd,
+ 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9,
+ 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+ };
+ unsigned char okm[82] =
+ { 0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1,
+ 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d,
+ 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, 0x2f, 0xfe, 0x8f, 0xa3,
+ 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2,
+ 0x17, 0x3c, 0x48, 0x6e, 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c,
+ 0x7f, 0x9d, 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f,
+ 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, 0xd3, 0xb4
+ };
+ char result[84];
+ int l = 82;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, salt, sizeof (salt),
+ ikm, sizeof (ikm), info, sizeof (info), NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc6 ()
+{
+ unsigned char ikm[22] =
+ { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b,
+ 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b
+ };
+ unsigned char okm[42] =
+ { 0x0a, 0xc1, 0xaf, 0x70, 0x02, 0xb3, 0xd7, 0x61, 0xd1, 0xe5,
+ 0x52, 0x98, 0xda, 0x9d, 0x05, 0x06, 0xb9, 0xae, 0x52, 0x05, 0x72, 0x20,
+ 0xa3, 0x06, 0xe0, 0x7b, 0x6b, 0x87, 0xe8, 0xdf, 0x21, 0xd0, 0xea, 0x00,
+ 0x03, 0x3d, 0xe0, 0x39, 0x84, 0xd3, 0x49, 0x18
+ };
+ char result[44];
+ int l = 42;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, NULL, 0, ikm,
+ sizeof (ikm), NULL, 0, NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc7 ()
+{
+ unsigned char ikm[80] =
+ { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
+ 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21,
+ 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,
+ 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45,
+ 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f
+ };
+ unsigned char salt[80] =
+ { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
+ 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75,
+ 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81,
+ 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+ 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99,
+ 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5,
+ 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf
+ };
+ unsigned char info1[34] = { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3,
+ 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ 0xd0, 0xd1
+ };
+ unsigned char info2[46] = { 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9,
+ 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5,
+ 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1,
+ 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd,
+ 0xfe, 0xff
+ };
+ unsigned char okm[82] =
+ { 0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1,
+ 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, 0x89, 0x9d,
+ 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, 0x2f, 0xfe, 0x8f, 0xa3,
+ 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2,
+ 0x17, 0x3c, 0x48, 0x6e, 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c,
+ 0x7f, 0x9d, 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f,
+ 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, 0xd3, 0xb4
+ };
+ char result[84];
+ int l = 82;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA1, GCRY_MD_SHA1, salt, sizeof (salt),
+ ikm, sizeof (ikm), info1, sizeof (info1), info2,
+ sizeof (info2), NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+void
+tc8 ()
+{
+ unsigned char ikm[32] =
+ { 0xbf, 0x16, 0x6e, 0x46, 0x3a, 0x6c, 0xf3, 0x93, 0xa7, 0x72,
+ 0x11, 0xa1, 0xdc, 0x0b, 0x07, 0xdb, 0x1a, 0x5e, 0xd9, 0xb9, 0x81, 0xbe,
+ 0xea, 0xe4, 0x31, 0x5f, 0x24, 0xff, 0xfe, 0x50, 0x8a, 0xde
+ };
+ unsigned char salt[4] = { 0xfc, 0x62, 0x76, 0x35 };
+ unsigned char info[86] =
+ { 0x8c, 0x0d, 0xcf, 0xb3, 0x25, 0x6e, 0x88, 0x0d, 0xc1, 0x0b,
+ 0x1d, 0x33, 0x15, 0x3e, 0x52, 0x0b, 0xb0, 0x77, 0xff, 0x7d, 0xc3, 0xc7,
+ 0xef, 0xe5, 0x8e, 0x3c, 0xc4, 0x4e, 0x8b, 0x41, 0x46, 0x1f, 0x02, 0x94,
+ 0x82, 0x35, 0xc5, 0xa6, 0x5e, 0x91, 0xd8, 0xa2, 0x90, 0xfd, 0x6f, 0xb4,
+ 0x07, 0xc9, 0xed, 0x6b, 0x18, 0x90, 0x31, 0xab, 0x0f, 0xb5, 0x6b, 0xec,
+ 0x9e, 0x45, 0xa2, 0x83, 0x65, 0x41, 0x69, 0x6e, 0x69, 0x74, 0x69, 0x61,
+ 0x6c, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x76, 0x65, 0x63,
+ 0x74, 0x6f, 0x72, 0x00
+ };
+ unsigned char okm[16] =
+ { 0xd6, 0x90, 0xec, 0x9e, 0x62, 0xdf, 0xb9, 0x41, 0xff, 0x92,
+ 0x4f, 0xd2, 0xf6, 0x1d, 0x67, 0xe0
+ };
+ char result[18];
+ int l = 16;
+
+ memset (result, 0, sizeof (result));
+ GNUNET_assert (GNUNET_CRYPTO_hkdf
+ (result, l, GCRY_MD_SHA512, GCRY_MD_SHA256, salt,
+ sizeof (salt), ikm, sizeof (ikm), info, sizeof (info),
+ NULL) == GNUNET_YES);
+ GNUNET_assert (memcmp (result, okm, l) == 0);
+ GNUNET_assert (memcmp (result + l, "\0", 2) == 0);
+}
+
+int
+main ()
+{
+ GNUNET_log_setup ("test-crypto-hkdf", "WARNING", NULL);
+
+ /* Official test vectors */
+ tc1 ();
+ tc2 ();
+ tc3 ();
+ tc4 ();
+ tc5 ();
+ tc6 ();
+
+ /* Additional tests */
+ tc7 ();
+ tc8 ();
+
+ return 0;
+}
diff --git a/src/util/test_crypto_ksk.c b/src/util/test_crypto_ksk.c
new file mode 100644
index 0000000..58c4595
--- /dev/null
+++ b/src/util/test_crypto_ksk.c
@@ -0,0 +1,260 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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
+testCorrectKey ()
+{
+ const char *want =
+ "010601000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b73c215f7a5e6b09bec55713c901786c09324a150980e014bdb0d04426934929c3b4971a9711af5455536cd6eeb8bfa004ee904972a737455f53c752987d8c82b755bc02882b44950c4acdc1672ba74c3b94d81a4c1ea3d74e7700ae5594c3a4f3c559e4bff2df6844fac302e4b66175e14dc8bad3ce44281d2fec1a1abef06301010000";
+ GNUNET_HashCode in;
+ struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
+ struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pkey;
+ int i;
+ char out[3];
+
+ FPRINTF (stderr, "%s", "Testing KBlock key correctness");
+ GNUNET_CRYPTO_hash ("X", strlen ("X"), &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);
+ GNUNET_CRYPTO_rsa_key_free (hostkey);
+#if 0
+ for (i = 0; i < sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded); i++)
+ printf ("%02x", ((unsigned char *) &pkey)[i]);
+ printf ("\n");
+#endif
+ for (i = 0; i < sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded); i++)
+ {
+ snprintf (out, sizeof (out), "%02x", ((unsigned char *) &pkey)[i]);
+ if (0 != strncmp (out, &want[i * 2], 2))
+ {
+ FPRINTF (stderr, " Failed! Wanted %.2s but got %2s at %d\n", &want[i * 2],
+ out, i);
+ return GNUNET_SYSERR;
+ }
+ }
+ FPRINTF (stderr, "%s", " OK\n");
+ return GNUNET_OK;
+}
+
+
+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<sizeof(struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded);i++)
+ * printf("%02x", ((unsigned char*) &pkey)[i]);
+ * printf("\n"); */
+ GNUNET_CRYPTO_rsa_key_free (hostkey);
+ for (i = 0; i < UNIQUE_ITER; i++)
+ {
+ FPRINTF (stderr, "%s", ".");
+ hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
+ if (hostkey == NULL)
+ {
+ GNUNET_break (0);
+ FPRINTF (stderr, "%s", " ERROR\n");
+ return GNUNET_SYSERR;
+ }
+ GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey1);
+ GNUNET_CRYPTO_rsa_key_free (hostkey);
+ if (0 !=
+ memcmp (&pkey, &pkey1,
+ sizeof (struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded)))
+ {
+ GNUNET_break (0);
+ FPRINTF (stderr, "%s", " ERROR\n");
+ return GNUNET_SYSERR;
+ }
+ }
+ FPRINTF (stderr, "%s", " OK\n");
+ return GNUNET_OK;
+}
+
+
+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, "%s", "W");
+ GNUNET_CRYPTO_rsa_key_get_public (hostkey, &pkey);
+
+ ok = 0;
+ start = GNUNET_TIME_absolute_get ();
+ for (i = 0; i < ITER; i++)
+ {
+ FPRINTF (stderr, "%s", ".");
+ if (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_rsa_encrypt (TESTSTRING, strlen (TESTSTRING) + 1, &pkey,
+ &target))
+ {
+ FPRINTF (stderr, "%s", "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
+ ok++;
+ continue;
+ }
+ if (-1 ==
+ GNUNET_CRYPTO_rsa_decrypt (hostkey, &target, result,
+ strlen (TESTSTRING) + 1))
+ {
+ FPRINTF (stderr, "%s", "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).rel_value, ok);
+ if (ok == 0)
+ return GNUNET_OK;
+ else
+ return GNUNET_SYSERR;
+}
+
+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, "%s", "W");
+ 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, "%s", ".");
+ if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
+ {
+ FPRINTF (stderr, "%s", "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_PONG_OWN,
+ &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).rel_value);
+ return ok;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int failureCount = 0;
+ GNUNET_HashCode in;
+ struct GNUNET_CRYPTO_RsaPrivateKey *hostkey;
+
+ GNUNET_log_setup ("test-crypto-ksk", "WARNING", NULL);
+ if (GNUNET_OK != testCorrectKey ())
+ failureCount++;
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &in);
+ hostkey = GNUNET_CRYPTO_rsa_key_create_from_hash (&in);
+ if (hostkey == NULL)
+ {
+ printf ("\nGNUNET_CRYPTO_rsa_key_create_from_hash failed!\n");
+ return 1;
+ }
+ if (GNUNET_OK != testMultiKey ("foo"))
+ failureCount++;
+ if (GNUNET_OK != testMultiKey ("bar"))
+ failureCount++;
+ if (GNUNET_OK != testEncryptDecrypt (hostkey))
+ failureCount++;
+ if (GNUNET_OK != testSignVerify (hostkey))
+ failureCount++;
+ GNUNET_CRYPTO_rsa_key_free (hostkey);
+
+ if (failureCount != 0)
+ {
+ printf ("\n\n%d TESTS FAILED!\n\n", failureCount);
+ return -1;
+ }
+ return 0;
+}
diff --git a/src/util/test_crypto_random.c b/src/util/test_crypto_random.c
new file mode 100644
index 0000000..681947a
--- /dev/null
+++ b/src/util/test_crypto_random.c
@@ -0,0 +1,71 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_random.c
+ * @brief testcase for crypto_random.c
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_crypto_lib.h"
+
+static int
+test (enum GNUNET_CRYPTO_Quality mode)
+{
+
+ int buf[1024];
+ unsigned int *b2;
+ int i;
+ unsigned long long n;
+
+ for (i = 0; i < 1024; i++)
+ GNUNET_break (1024 > (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, "%s", "!");
+ 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 0000000..f6800af
--- /dev/null
+++ b/src/util/test_crypto_rsa.c
@@ -0,0 +1,334 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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, "%s", "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, "%s", ".");
+ if (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_rsa_encrypt (TESTSTRING, strlen (TESTSTRING) + 1, &pkey,
+ &target))
+ {
+ FPRINTF (stderr, "%s", "GNUNET_CRYPTO_rsa_encrypt returned SYSERR\n");
+ ok++;
+ continue;
+ }
+ if (-1 ==
+ GNUNET_CRYPTO_rsa_decrypt (hostkey, &target, result,
+ strlen (TESTSTRING) + 1))
+ {
+ FPRINTF (stderr, "%s", "GNUNET_CRYPTO_rsa_decrypt returned SYSERR\n");
+ ok++;
+ continue;
+
+ }
+ if (strncmp (TESTSTRING, result, strlen (TESTSTRING)) != 0)
+ {
+ printf ("%s != %.*s - testEncryptDecrypt failed!\n", TESTSTRING,
+ (int) 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).rel_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, "%s", "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, "%s", ".");
+ if (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_rsa_encrypt (TESTSTRING, strlen (TESTSTRING) + 1, &pkey,
+ &target))
+ {
+ FPRINTF (stderr, "%s", "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).rel_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, "%s", "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, "%s", ".");
+ GNUNET_CRYPTO_aes_create_session_key (&insk);
+ if (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_rsa_encrypt (&insk,
+ sizeof (struct GNUNET_CRYPTO_AesSessionKey),
+ &pkey, &target))
+ {
+ FPRINTF (stderr, "%s", "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, "%s", "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 %llums (%d failures)\n", ITER,
+ (unsigned long long)
+ GNUNET_TIME_absolute_get_duration (start).rel_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, "%s", "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, "%s", ".");
+ if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
+ {
+ FPRINTF (stderr, "%s", "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_PONG_OWN,
+ &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).rel_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, "%s", "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, "%s", ".");
+ if (GNUNET_SYSERR == GNUNET_CRYPTO_rsa_sign (hostkey, &purp, &sig))
+ {
+ FPRINTF (stderr, "%s", "GNUNET_CRYPTO_rsa_sign returned SYSERR\n");
+ ok = GNUNET_SYSERR;
+ continue;
+ }
+ }
+ printf ("%d RSA sign operations %llu ms\n", ITER,
+ (unsigned long long)
+ GNUNET_TIME_absolute_get_duration (start).rel_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_assert (NULL != key);
+ GNUNET_CRYPTO_rsa_key_get_public (key, &p1);
+ GNUNET_CRYPTO_rsa_key_free (key);
+ key = GNUNET_CRYPTO_rsa_key_create_from_file (KEYFILE);
+ GNUNET_assert (NULL != key);
+ 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_assert (NULL != key);
+ 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 0000000..5462772
--- /dev/null
+++ b/src/util/test_disk.c
@@ -0,0 +1,283 @@
+/*
+ This file is part of GNUnet.
+ (C) 2001, 2002, 2003, 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 (strlen (TESTSTRING) !=
+ GNUNET_DISK_fn_write (".testfile", TESTSTRING, strlen (TESTSTRING),
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE))
+ return 1;
+ if (GNUNET_OK != GNUNET_DISK_file_test (".testfile"))
+ return 1;
+ ret = GNUNET_DISK_fn_read (".testfile", tmp, sizeof (tmp) - 1);
+ 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_fn_read (".testfile2", tmp, sizeof (tmp) - 1);
+ 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 ()
+{
+ struct GNUNET_DISK_FileHandle *fh;
+ uint64_t size;
+ long avail;
+
+ fh = GNUNET_DISK_file_open (".testfile",
+ GNUNET_DISK_OPEN_READWRITE |
+ GNUNET_DISK_OPEN_CREATE,
+ GNUNET_DISK_PERM_USER_READ |
+ GNUNET_DISK_PERM_USER_WRITE);
+ GNUNET_assert (GNUNET_NO == GNUNET_DISK_handle_invalid (fh));
+ GNUNET_break (5 == GNUNET_DISK_file_write (fh, "Hello", 5));
+ GNUNET_DISK_file_close (fh);
+ 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, GNUNET_NO);
+ avail = GNUNET_DISK_get_blocks_available (".testfile");
+ GNUNET_log_skip (0, GNUNET_NO);
+ fh = GNUNET_DISK_file_open (".testfile",
+ GNUNET_DISK_OPEN_READWRITE |
+ GNUNET_DISK_OPEN_CREATE,
+ GNUNET_DISK_PERM_USER_WRITE |
+ GNUNET_DISK_PERM_USER_READ);
+ GNUNET_assert (GNUNET_NO == GNUNET_DISK_handle_invalid (fh));
+ while ((avail == GNUNET_DISK_get_blocks_available (".testfile")) &&
+ (avail != -1))
+ if (16 != GNUNET_DISK_file_write (fh, "HelloWorld123456", 16))
+ {
+ GNUNET_DISK_file_close (fh);
+ GNUNET_break (0 == UNLINK (".testfile"));
+ return 1;
+ }
+ GNUNET_DISK_file_close (fh);
+ 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" DIR_SEPARATOR_STR "entry"))
+ return 1;
+ if (GNUNET_OK !=
+ GNUNET_DISK_directory_create ("test" DIR_SEPARATOR_STR "entry_more"))
+ return 1;
+ GNUNET_DISK_directory_scan ("test", &scan_callback,
+ "test" DIR_SEPARATOR_STR "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 (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/test-gnunet-disk-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/test-gnunet-disk-a/b/c/d/e", fn);
+ GNUNET_free (fn);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_directory_remove ("/tmp/test-gnunet-disk-a"));
+ 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, GNUNET_NO);
+ 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 0000000..a517887
--- /dev/null
+++ b/src/util/test_getopt.c
@@ -0,0 +1,216 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 ()
+{
+ char *const emptyargv[] = {
+ "test",
+ NULL
+ };
+ const struct GNUNET_GETOPT_CommandLineOption emptyoptionlist[] = {
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (1 != GNUNET_GETOPT_run ("test", emptyoptionlist, 1, emptyargv))
+ return 1;
+
+ return 0;
+}
+
+static int
+testVerbose ()
+{
+ 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
+ };
+
+ if (3 != GNUNET_GETOPT_run ("test", verboseoptionlist, 4, myargv))
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ if (vflags != 2)
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+testVersion ()
+{
+ char *const myargv[] = {
+ "test_getopt",
+ "-v",
+ NULL
+ };
+ const struct GNUNET_GETOPT_CommandLineOption versionoptionlist[] = {
+ GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION),
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (-1 != GNUNET_GETOPT_run ("test_getopt", versionoptionlist, 2, myargv))
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+testAbout ()
+{
+ char *const myargv[] = {
+ "test_getopt",
+ "-h",
+ NULL
+ };
+ const struct GNUNET_GETOPT_CommandLineOption aboutoptionlist[] = {
+ GNUNET_GETOPT_OPTION_HELP ("Testing"),
+ GNUNET_GETOPT_OPTION_END
+ };
+
+ if (-1 != GNUNET_GETOPT_run ("test_getopt", aboutoptionlist, 2, myargv))
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ return 0;
+}
+
+static int
+testLogOpts ()
+{
+ 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
+ };
+
+ if (5 != GNUNET_GETOPT_run ("test_getopt", logoptionlist, 5, myargv))
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ GNUNET_assert (fn != NULL);
+ 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 ()
+{
+ 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
+ };
+
+ if (6 != GNUNET_GETOPT_run ("test_getopt", logoptionlist, 6, myargv))
+ {
+ GNUNET_break (0);
+ return 1;
+ }
+ 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 */
+#ifndef MINGW
+ GNUNET_break (0 == CLOSE (1));
+#endif
+ 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_os_network.c b/src/util/test_os_network.c
new file mode 100644
index 0000000..4486e6a
--- /dev/null
+++ b/src/util/test_os_network.c
@@ -0,0 +1,86 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 1
+
+/**
+ * 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,
+ const struct sockaddr *broadcast_addr, const struct sockaddr *netmask,
+ socklen_t addrlen)
+{
+ int *ok = cls;
+ char buf[INET6_ADDRSTRLEN];
+
+ if (NULL == addr)
+ return GNUNET_OK;
+#if VERBOSE
+ const char * protocol;
+ if (addrlen == sizeof (struct sockaddr_in))
+ protocol = "IPv4";
+ else
+ protocol = "IPv6";
+ printf ("%s Address `%s'\n", protocol, GNUNET_a2s ((const struct sockaddr *) addr,addrlen) );
+ printf (" Netmask `%s'\n", GNUNET_a2s ((const struct sockaddr *) netmask, addrlen) );
+ printf (" Broadcast `%s'\n", GNUNET_a2s ((const struct sockaddr *) broadcast_addr,addrlen) );
+#endif
+
+ 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 0000000..94e2719
--- /dev/null
+++ b/src/util/test_os_priority.c
@@ -0,0 +1,69 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 ()
+{
+ if (GNUNET_OK !=
+ GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ GNUNET_SCHEDULER_PRIORITY_DEFAULT))
+ return 1;
+ if (GNUNET_OK !=
+ GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ GNUNET_SCHEDULER_PRIORITY_UI))
+ return 1;
+ if (GNUNET_OK !=
+ GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ GNUNET_SCHEDULER_PRIORITY_IDLE))
+ return 1;
+ if (GNUNET_OK !=
+ GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ GNUNET_SCHEDULER_PRIORITY_BACKGROUND))
+ return 1;
+ if (GNUNET_OK !=
+ GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ GNUNET_SCHEDULER_PRIORITY_HIGH))
+ return 1;
+ if (GNUNET_OK !=
+ GNUNET_OS_set_process_priority (GNUNET_OS_process_current (),
+ GNUNET_SCHEDULER_PRIORITY_HIGH))
+ return 1;
+ 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_os_start_process.c b/src/util/test_os_start_process.c
new file mode 100644
index 0000000..54638c1
--- /dev/null
+++ b/src/util/test_os_start_process.c
@@ -0,0 +1,194 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_start_process.c
+ * @brief testcase for os start process code
+ *
+ * This testcase simply calls the os start process code
+ * giving a file descriptor to write stdout to. If the
+ * correct data "HELLO" is read then all is well.
+ */
+#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 "disk.h"
+
+#define VERBOSE GNUNET_NO
+
+static char *test_phrase = "HELLO WORLD";
+static int ok;
+
+static struct GNUNET_OS_Process *proc;
+
+/* Pipe to write to started processes stdin (on write end) */
+static struct GNUNET_DISK_PipeHandle *hello_pipe_stdin;
+
+/* Pipe to read from started processes stdout (on read end) */
+static struct GNUNET_DISK_PipeHandle *hello_pipe_stdout;
+
+static GNUNET_SCHEDULER_TaskIdentifier die_task;
+
+static void
+end_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+
+ if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
+ }
+ GNUNET_OS_process_wait (proc);
+ GNUNET_OS_process_close (proc);
+ proc = NULL;
+ GNUNET_DISK_pipe_close (hello_pipe_stdout);
+ GNUNET_DISK_pipe_close (hello_pipe_stdin);
+}
+
+static void
+read_call (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_DISK_FileHandle *stdout_read_handle = cls;
+ char buf[16];
+
+ memset (&buf, 0, sizeof (buf));
+ int bytes;
+
+ bytes = GNUNET_DISK_file_read (stdout_read_handle, &buf, sizeof (buf));
+
+#if VERBOSE
+ FPRINTF (stderr, "bytes is %d\n", bytes);
+#endif
+
+ if (bytes < 1)
+ {
+ GNUNET_break (0);
+ ok = 1;
+ GNUNET_SCHEDULER_cancel (die_task);
+ GNUNET_SCHEDULER_add_now (&end_task, NULL);
+ return;
+ }
+
+ ok = strncmp (&buf[0], test_phrase, strlen (test_phrase));
+#if VERBOSE
+ FPRINTF (stderr, "read %s\n", &buf[0]);
+#endif
+ if (ok == 0)
+ {
+ GNUNET_SCHEDULER_cancel (die_task);
+ GNUNET_SCHEDULER_add_now (&end_task, NULL);
+ return;
+ }
+
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ stdout_read_handle, &read_call,
+ stdout_read_handle);
+
+}
+
+
+static void
+task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ char *fn;
+ const struct GNUNET_DISK_FileHandle *stdout_read_handle;
+ const struct GNUNET_DISK_FileHandle *wh;
+
+ GNUNET_asprintf (&fn, "cat");
+
+ hello_pipe_stdin = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_YES, GNUNET_NO);
+ hello_pipe_stdout = GNUNET_DISK_pipe (GNUNET_YES, GNUNET_YES, GNUNET_NO, GNUNET_YES);
+
+ if ((hello_pipe_stdout == NULL) || (hello_pipe_stdin == NULL))
+ {
+ GNUNET_break (0);
+ ok = 1;
+ GNUNET_free (fn);
+ return;
+ }
+
+ proc =
+ GNUNET_OS_start_process (GNUNET_NO, hello_pipe_stdin, hello_pipe_stdout, fn,
+ "test_gnunet_echo_hello", "-", NULL);
+ GNUNET_free (fn);
+
+ /* Close the write end of the read pipe */
+ GNUNET_DISK_pipe_close_end (hello_pipe_stdout, GNUNET_DISK_PIPE_END_WRITE);
+ /* Close the read end of the write pipe */
+ GNUNET_DISK_pipe_close_end (hello_pipe_stdin, GNUNET_DISK_PIPE_END_READ);
+
+ wh = GNUNET_DISK_pipe_handle (hello_pipe_stdin, GNUNET_DISK_PIPE_END_WRITE);
+
+ /* Write the test_phrase to the cat process */
+ if (GNUNET_DISK_file_write (wh, test_phrase, strlen (test_phrase) + 1) !=
+ strlen (test_phrase) + 1)
+ {
+ GNUNET_break (0);
+ ok = 1;
+ return;
+ }
+
+ /* Close the write end to end the cycle! */
+ GNUNET_DISK_pipe_close_end (hello_pipe_stdin, GNUNET_DISK_PIPE_END_WRITE);
+
+ stdout_read_handle =
+ GNUNET_DISK_pipe_handle (hello_pipe_stdout, GNUNET_DISK_PIPE_END_READ);
+
+ die_task =
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MINUTES, 1), &end_task,
+ NULL);
+
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+ stdout_read_handle, &read_call,
+ (void *) stdout_read_handle);
+
+}
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+check ()
+{
+ ok = 1;
+ GNUNET_SCHEDULER_run (&task, &ok);
+ return ok;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret;
+
+ GNUNET_log_setup ("test-os-start-process",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret = check ();
+
+ return ret;
+}
diff --git a/src/util/test_peer.c b/src/util/test_peer.c
new file mode 100644
index 0000000..2a48401
--- /dev/null
+++ b/src/util/test_peer.c
@@ -0,0 +1,139 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_peer.c
+ * @brief testcase for peer.c
+ * @author Safey Mohammed
+ */
+
+#include "platform.h"
+#include "gnunet_crypto_lib.h"
+#include "gnunet_peer_lib.h"
+
+#define NUMBER_OF_PEERS 10
+
+#define VERBOSE GNUNET_NO
+
+/**
+ * A list of Peer ID's to play with
+ */
+static struct GNUNET_PeerIdentity pidArr[NUMBER_OF_PEERS];
+
+
+static void
+generatePeerIdList ()
+{
+ int i;
+
+ for (i = 0; i < NUMBER_OF_PEERS; i++)
+ {
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
+ &pidArr[i].hashPubKey);
+#if VERBOSE
+ printf ("Peer %d: %s\n", i, GNUNET_i2s (&pidArr[i]));
+#endif
+ }
+}
+
+
+static int
+check ()
+{
+ int i;
+ GNUNET_PEER_Id pid;
+ struct GNUNET_PeerIdentity res;
+ struct GNUNET_PeerIdentity zero;
+ GNUNET_PEER_Id ids[] = { 1, 2, 3 };
+
+ GNUNET_assert (0 == GNUNET_PEER_intern (NULL));
+ /* Insert Peers into PeerEntry table and hashmap */
+ for (i = 0; i < NUMBER_OF_PEERS; i++)
+ {
+ pid = GNUNET_PEER_intern (&pidArr[i]);
+ if (pid != (i + 1))
+ {
+ FPRINTF (stderr, "%s", "Unexpected Peer ID returned by intern function\n");
+ return 1;
+ }
+ }
+
+ /* Referencing the first 3 peers once again */
+ for (i = 0; i < 3; i++)
+ {
+ pid = GNUNET_PEER_intern (&pidArr[i]);
+ if (pid != (i + 1))
+ {
+ FPRINTF (stderr, "%s", "Unexpected Peer ID returned by intern function\n");
+ return 1;
+ }
+ }
+
+ /* Dereferencing the first 3 peers once [decrementing their reference count] */
+ GNUNET_PEER_decrement_rcs (ids, 3);
+
+ /* re-referencing the first 3 peers using the change_rc function */
+ for (i = 1; i <= 3; i++)
+ GNUNET_PEER_change_rc (i, 1);
+
+ /* Removing the second Peer from the PeerEntry hash map */
+ GNUNET_PEER_change_rc (2, -2);
+
+ /* convert the pid of the first PeerEntry into that of the third */
+ GNUNET_PEER_resolve (1, &res);
+ GNUNET_assert (0 == memcmp (&res, &pidArr[0], sizeof (res)));
+
+ /*
+ * Attempt to convert pid = 0 (which is reserved)
+ * into a peer identity object, the peer identity memory
+ * is expected to be set to zero
+ */
+ memset (&zero, 0, sizeof (struct GNUNET_PeerIdentity));
+ GNUNET_log_skip (1, GNUNET_YES);
+ GNUNET_PEER_resolve (0, &res);
+ GNUNET_assert (0 == memcmp (&res, &zero, sizeof (res)));
+
+ /* Removing peer entries 1 and 3 from table using the list decrement function */
+ /* If count = 0, nothing should be done whatsoever */
+ GNUNET_PEER_decrement_rcs (ids, 0);
+
+ ids[1] = 3;
+ GNUNET_PEER_decrement_rcs (ids, 2);
+ GNUNET_PEER_decrement_rcs (ids, 2);
+
+ return 0;
+}
+
+
+int
+main ()
+{
+ int i;
+
+ GNUNET_log_setup ("test-peer", "ERROR", NULL);
+ for (i = 0; i < 1; i++)
+ {
+ generatePeerIdList ();
+ if (0 != check ())
+ return 1;
+ }
+ return 0;
+}
+
+/* end of test_peer.c */
diff --git a/src/util/test_plugin.c b/src/util/test_plugin.c
new file mode 100644
index 0000000..428cdaf
--- /dev/null
+++ b/src/util/test_plugin.c
@@ -0,0 +1,79 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 void
+test_cb (void *cls, const char *libname, void *lib_ret)
+{
+ void *ret;
+
+ GNUNET_assert (0 == strcmp (cls, "test"));
+ GNUNET_assert (0 == strcmp (lib_ret, "Hello"));
+ ret = GNUNET_PLUGIN_unload (libname, "out");
+ GNUNET_assert (NULL != ret);
+ GNUNET_assert (0 == strcmp (ret, "World"));
+}
+
+
+static int
+check ()
+{
+ void *ret;
+
+ GNUNET_log_skip (1, GNUNET_NO);
+ ret = GNUNET_PLUGIN_load ("libgnunet_plugin_missing", NULL);
+ GNUNET_log_skip (0, GNUNET_NO);
+ 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);
+
+ GNUNET_PLUGIN_load_all ("libgnunet_plugin_tes", "in", &test_cb, "test");
+ 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 0000000..beb78d2
--- /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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 0000000..faeb4e7
--- /dev/null
+++ b/src/util/test_program.c
@@ -0,0 +1,121 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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 setme1, setme2;
+
+static struct GNUNET_GETOPT_CommandLineOption options1[] = {
+ {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme1},
+ GNUNET_GETOPT_OPTION_END
+};
+
+static struct GNUNET_GETOPT_CommandLineOption options2[] = {
+ {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme1},
+ {'N', "number", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme2},
+ GNUNET_GETOPT_OPTION_END
+};
+
+static struct GNUNET_GETOPT_CommandLineOption options3[] = {
+ {'N', "number", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme1},
+ {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme2},
+ GNUNET_GETOPT_OPTION_END
+};
+
+static struct GNUNET_GETOPT_CommandLineOption options4[] = {
+ {'n', "name", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme1},
+ {'n', "number", NULL, "description", 0, &GNUNET_GETOPT_set_one, &setme2},
+ GNUNET_GETOPT_OPTION_END
+};
+
+/**
+ * Main function that will be run.
+ */
+
+static void
+runner (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ int *ok = cls;
+
+ GNUNET_assert (setme1 == 1);
+ 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",
+ options1, &runner, &ok));
+
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_PROGRAM_run (7, argv, "test_program", "A test",
+ options2, &runner, &ok));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_PROGRAM_run (7, argv, "test_program", "A test",
+ options3, &runner, &ok));
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_PROGRAM_run (7, argv, "test_program", "A test",
+ options4, &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 0000000..e69de29
--- /dev/null
+++ b/src/util/test_program_data.conf
diff --git a/src/util/test_pseudonym.c b/src/util/test_pseudonym.c
new file mode 100644
index 0000000..20a3d3d
--- /dev/null
+++ b/src/util/test_pseudonym.c
@@ -0,0 +1,191 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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) do { if (!(a)) { ok = GNUNET_NO; GNUNET_break(0); goto FAILURE; } } while (0)
+
+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;
+}
+
+static int
+fake_noti_callback (void *cls, const GNUNET_HashCode * pseudonym,
+ const struct GNUNET_CONTAINER_MetaData *md, int rating)
+{
+ int *ret = cls;
+
+ (*ret)++;
+ return GNUNET_OK;
+}
+
+static int
+false_callback (void *cls, const GNUNET_HashCode * pseudonym,
+ const struct GNUNET_CONTAINER_MetaData *md, int rating)
+{
+ return GNUNET_OK;
+}
+
+int
+main (int argc, char *argv[])
+{
+ int ok;
+ GNUNET_HashCode rid1;
+ GNUNET_HashCode id2;
+ GNUNET_HashCode rid2;
+ GNUNET_HashCode fid;
+ GNUNET_HashCode id3;
+
+ int old;
+ int newVal;
+ struct GNUNET_CONFIGURATION_Handle *cfg;
+ char *name1;
+ char *name2;
+ char *name3;
+ char *noname;
+ int notiCount, fakenotiCount;
+ int count;
+ static char m[1024 * 1024 * 10];
+
+ memset (m, 'b', sizeof (m));
+ m[sizeof (m) - 1] = '\0';
+
+ GNUNET_log_setup ("test-pseudonym", "WARNING", NULL);
+ ok = GNUNET_YES;
+ GNUNET_CRYPTO_random_disable_entropy_gathering ();
+ (void) 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;
+ fakenotiCount = 0;
+ count = 0;
+ GNUNET_PSEUDONYM_discovery_callback_register (cfg, &fake_noti_callback,
+ &fakenotiCount);
+ GNUNET_PSEUDONYM_discovery_callback_register (cfg, &noti_callback,
+ &notiCount);
+ GNUNET_PSEUDONYM_discovery_callback_unregister (&false_callback, &count);
+ GNUNET_PSEUDONYM_discovery_callback_unregister (&fake_noti_callback,
+ &fakenotiCount);
+
+ /* ACTUAL TEST CODE */
+ old = GNUNET_PSEUDONYM_list_all (cfg, NULL, NULL);
+ meta = GNUNET_CONTAINER_meta_data_create ();
+ GNUNET_CONTAINER_meta_data_insert (meta, "<test>", EXTRACTOR_METATYPE_TITLE,
+ EXTRACTOR_METAFORMAT_UTF8, "text/plain",
+ "test", strlen ("test") + 1);
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &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 (GNUNET_CRYPTO_QUALITY_WEAK, &id2);
+ GNUNET_PSEUDONYM_add (cfg, &id2, meta);
+ CHECK (notiCount == 3);
+ newVal = GNUNET_PSEUDONYM_list_all (cfg, &iter, &ok);
+ CHECK (old < newVal);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_CONTAINER_meta_data_insert (meta, "<test>",
+ EXTRACTOR_METATYPE_COMMENT,
+ EXTRACTOR_METAFORMAT_UTF8,
+ "text/plain", m,
+ strlen (m) + 1));
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &id3);
+ GNUNET_PSEUDONYM_add (cfg, &id3, meta);
+ name3 = GNUNET_PSEUDONYM_id_to_name (cfg, &id3);
+ 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_SYSERR == GNUNET_PSEUDONYM_name_to_id (cfg, "fake", &rid2));
+ 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)));
+
+ GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK, &fid);
+ GNUNET_log_skip (1, GNUNET_NO);
+ CHECK (0 == GNUNET_PSEUDONYM_rank (cfg, &fid, 0));
+ GNUNET_log_skip (0, GNUNET_YES);
+ noname = GNUNET_PSEUDONYM_id_to_name (cfg, &fid);
+ CHECK (noname != NULL);
+ 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);
+ GNUNET_free (name3);
+ GNUNET_free (noname);
+ /* END OF TEST CODE */
+FAILURE:
+ GNUNET_PSEUDONYM_discovery_callback_unregister (&noti_callback, &notiCount);
+ GNUNET_CONTAINER_meta_data_destroy (meta);
+ GNUNET_CONFIGURATION_destroy (cfg);
+ GNUNET_break (GNUNET_OK ==
+ GNUNET_DISK_directory_remove ("/tmp/gnunet-pseudonym-test"));
+ 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 0000000..5523007
--- /dev/null
+++ b/src/util/test_pseudonym_data.conf
@@ -0,0 +1,7 @@
+# General settings
+[client]
+HOME = "/tmp/gnunet-pseudonym-test"
+
+[TESTING]
+WEAKRANDOM = YES
+
diff --git a/src/util/test_resolver_api.c b/src/util/test_resolver_api.c
new file mode 100644
index 0000000..67d5f46
--- /dev/null
+++ b/src/util/test_resolver_api.c
@@ -0,0 +1,437 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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
+
+/**
+ * Using DNS root servers to check gnunet's resolver service
+ * a.root-servers.net <-> 198.41.0.4 is a fix 1:1 mapping that should not change over years
+ * For more information have a look at IANA's website http://www.root-servers.org/
+ */
+#define ROOTSERVER_NAME "a.root-servers.net"
+#define ROOTSERVER_IP "198.41.0.4"
+
+static void
+check_hostname (void *cls, const struct sockaddr *sa, socklen_t salen)
+{
+ int *ok = cls;
+
+ if (salen == 0)
+ {
+ (*ok) &= ~8;
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Got IP address `%s' for our host.\n"),
+ GNUNET_a2s (sa, salen));
+}
+
+
+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
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received invalid hostname `%s'.\n",
+ hostname);
+ 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
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Received unexpected hostname `%s', expected `localhost' (this could be OK).\n",
+ hostname);
+ }
+}
+
+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
+ {
+ char buf[INET_ADDRSTRLEN];
+
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Received incorrect address`%s'.\n",
+ inet_ntop (AF_INET, &sai->sin_addr, buf, sizeof (buf)));
+ GNUNET_break (0);
+ }
+}
+
+static void
+check_local_fqdn (void *cls, const char *gnunet_fqdn)
+{
+ int result = 0;
+
+ struct hostent *host;
+ char hostname[GNUNET_OS_get_hostname_max_length () + 1];
+
+ if (0 != gethostname (hostname, sizeof (hostname) - 1))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK,
+ "gethostname");
+ return;
+ }
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Resolving our FQDN `%s'\n"),
+ hostname);
+#endif
+ host = gethostbyname (hostname);
+ if (NULL == host)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not resolve our FQDN: %s %u\n"), hstrerror (h_errno),
+ h_errno);
+ return;
+ }
+
+ GNUNET_assert (0 != host);
+
+ result = strcmp (host->h_name, gnunet_fqdn);
+ if (0 != result)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Local resolved and resolver resolved fqdns are not equal\n");
+ }
+ GNUNET_assert (0 == result);
+}
+
+
+
+static void
+check_rootserver_ip (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 (0 == strcmp (inet_ntoa (sai->sin_addr), ROOTSERVER_IP))
+ {
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received correct rootserver ip address.\n");
+#endif
+ (*ok) &= ~1;
+ }
+ else
+ {
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received incorrect rootserver ip address.\n");
+#endif
+ GNUNET_break (0);
+ }
+}
+
+static void
+check_rootserver_name (void *cls, const char *hostname)
+{
+ int *ok = cls;
+
+ if (hostname == NULL)
+ return;
+
+ if (0 == strcmp (hostname, ROOTSERVER_NAME))
+ {
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received correct rootserver hostname `%s'.\n", hostname);
+#endif
+ (*ok) &= ~2;
+ }
+ else
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Received invalid rootserver hostname `%s'.\n", hostname);
+ GNUNET_break (0);
+ }
+}
+
+static void
+run (void *cls, char *const *args, const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ int *ok = cls;
+ struct sockaddr_in sa;
+ struct GNUNET_TIME_Relative timeout =
+ GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30);
+ int count_ips = 0;
+ char *own_fqdn;
+
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = (u_char) sizeof (sa);
+#endif
+ sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ /*
+ * Looking up our own fqdn
+ */
+ own_fqdn = GNUNET_RESOLVER_local_fqdn_get ();
+ check_local_fqdn (NULL, own_fqdn);
+ GNUNET_free_non_null (own_fqdn);
+
+ /*
+ * Testing non-local DNS resolution
+ * DNS rootserver to test: a.root-servers.net - 198.41.0.4
+ */
+ const char *rootserver_name = ROOTSERVER_NAME;
+ struct hostent *rootserver;
+
+ rootserver = gethostbyname (rootserver_name);
+ if (rootserver == NULL)
+ {
+ /* Error: resolving ip addresses does not work */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("gethostbyname() could not lookup IP address: %s\n"),
+ hstrerror (h_errno));
+ FPRINTF (stderr,
+ "%s", "System seems to be off-line, will not run all DNS tests\n");
+ *ok = 0; /* mark test as passing anyway */
+ return;
+ }
+
+ /* Counting returned IP addresses */
+ while (rootserver->h_addr_list[count_ips] != NULL)
+ count_ips++;
+ if (count_ips > 1)
+ {
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "IP received range for root name server, but a root name server has only 1 IP\n");
+#endif
+ GNUNET_break (0);
+ }
+
+ /* Comparing to resolved address to the address the root name server should have */
+ if (strcmp
+ (inet_ntoa (*(struct in_addr *) rootserver->h_addr_list[0]),
+ ROOTSERVER_IP) != 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "IP received and IP for root name server differ\n");
+ GNUNET_break (0);
+ }
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "System's own forward name resolution is working\n");
+#endif
+
+ /* Resolve the same using GNUNET */
+ GNUNET_RESOLVER_ip_get (ROOTSERVER_NAME, AF_INET, timeout,
+ &check_rootserver_ip, cls);
+
+ /*
+ * Success: forward lookups work as expected
+ * Next step: reverse lookups
+ */
+
+ struct in_addr rootserver_addr;
+
+ rootserver->h_name = "";
+ if (1 != inet_pton (AF_INET, ROOTSERVER_IP, &rootserver_addr))
+ {
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Could not transform root name server IP address\n");
+#endif
+ GNUNET_break (0);
+ }
+
+ rootserver =
+ gethostbyaddr (&rootserver_addr, sizeof (rootserver_addr), AF_INET);
+ if (rootserver == NULL)
+ {
+ /* Error: resolving IP addresses does not work */
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("gethostbyaddr() could not lookup hostname: %s\n"),
+ hstrerror (h_errno));
+ GNUNET_break (0);
+ }
+ else
+ {
+ if (0 != strcmp (rootserver->h_name, ROOTSERVER_NAME))
+ {
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Received hostname and hostname for root name server differ\n");
+#endif
+ GNUNET_break (0);
+ }
+ }
+
+#if DEBUG_RESOLVER
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "System's own reverse name resolution is working\n");
+#endif
+
+ /* Resolve the same using GNUNET */
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = (u_char) sizeof (sa);
+#endif
+#ifndef MINGW
+ inet_aton (ROOTSERVER_IP, &sa.sin_addr);
+#else
+ sa.sin_addr.S_un.S_addr = inet_addr (ROOTSERVER_IP);
+#endif
+ GNUNET_RESOLVER_hostname_get ((const struct sockaddr *) &sa,
+ sizeof (struct sockaddr), GNUNET_YES, timeout,
+ &check_rootserver_name, cls);
+
+ memset (&sa, 0, sizeof (sa));
+ sa.sin_family = AF_INET;
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = (u_char) sizeof (sa);
+#endif
+ sa.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ GNUNET_RESOLVER_ip_get ("localhost", AF_INET, timeout, &check_127, cls);
+ GNUNET_RESOLVER_hostname_get ((const struct sockaddr *) &sa,
+ sizeof (struct sockaddr), GNUNET_YES, timeout,
+ &check_localhost, cls);
+
+ GNUNET_RESOLVER_hostname_get ((const struct sockaddr *) &sa,
+ sizeof (struct sockaddr), GNUNET_NO, timeout,
+ &check_localhost_num, cls);
+ GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, timeout, &check_hostname, cls);
+
+}
+
+static int
+check ()
+{
+ int ok = 1 + 2 + 4 + 8;
+ char *fn;
+ char *pfx;
+ struct GNUNET_OS_Process *proc;
+
+ 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 };
+ pfx = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_BINDIR);
+ GNUNET_asprintf (&fn, "%s%cgnunet-service-resolver", pfx, DIR_SEPARATOR);
+ GNUNET_free (pfx);
+ proc = GNUNET_OS_start_process (GNUNET_YES, NULL, NULL, fn, "gnunet-service-resolver",
+#if VERBOSE
+ "-L", "DEBUG",
+#endif
+ "-c", "test_resolver_api_data.conf", NULL);
+ GNUNET_assert (NULL != proc);
+ GNUNET_free (fn);
+ GNUNET_assert (GNUNET_OK ==
+ GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
+ argv, "test-resolver-api", "nohelp",
+ options, &run, &ok));
+ if (0 != GNUNET_OS_process_kill (proc, SIGTERM))
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
+ ok = 1;
+ }
+ GNUNET_OS_process_wait (proc);
+ GNUNET_OS_process_close (proc);
+ proc = NULL;
+ 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/util/test_resolver_api_data.conf b/src/util/test_resolver_api_data.conf
new file mode 100644
index 0000000..745cb7b
--- /dev/null
+++ b/src/util/test_resolver_api_data.conf
@@ -0,0 +1,8 @@
+[PATHS]
+SERVICEHOME = /tmp/test-gnunetd-statistics/
+
+[resolver]
+PORT = 22354
+HOSTNAME = localhost
+DEBUG = YES
+
diff --git a/src/util/test_scheduler.c b/src/util/test_scheduler.c
new file mode 100644
index 0000000..01982ee
--- /dev/null
+++ b/src/util/test_scheduler.c
@@ -0,0 +1,266 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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"
+#include "gnunet_disk_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+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 (GNUNET_SCHEDULER_PRIORITY_COUNT));
+ GNUNET_assert (3 == *ok);
+ (*ok) = 4;
+}
+
+
+static void
+task2 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ int *ok = cls;
+
+ GNUNET_assert (2 == *ok);
+ (*ok) = 3;
+ /* t3 will go before t4: higher priority */
+ GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_UI, &task3,
+ cls);
+}
+
+static void
+task4 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ int *ok = cls;
+
+ GNUNET_assert (4 == *ok);
+ (*ok) = 5;
+}
+
+struct GNUNET_DISK_PipeHandle *p;
+static const struct GNUNET_DISK_FileHandle *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 (GNUNET_NETWORK_fdset_handle_isset (tc->write_ready, fds[1]));
+ (*ok) = 7;
+ GNUNET_assert (1 == GNUNET_DISK_file_write (fds[1], &c, 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 (GNUNET_NETWORK_fdset_handle_isset (tc->read_ready, fds[0]));
+ GNUNET_assert (1 == GNUNET_DISK_file_read (fds[0], &c, 1));
+ (*ok) = 8;
+ GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, &taskLast,
+ cls);
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+static void
+task5 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ int *ok = cls;
+
+ GNUNET_assert (5 == *ok);
+ (*ok) = 6;
+ p = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO, GNUNET_NO);
+ GNUNET_assert (NULL != p);
+ fds[0] = GNUNET_DISK_pipe_handle (p, GNUNET_DISK_PIPE_END_READ);
+ fds[1] = GNUNET_DISK_pipe_handle (p, GNUNET_DISK_PIPE_END_WRITE);
+ GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, fds[0], &taskRd,
+ cls);
+ GNUNET_SCHEDULER_add_write_file (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 t4;
+
+ GNUNET_assert (1 == *ok);
+ (*ok) = 2;
+ /* t2 will go first -- prereq for all */
+ t2 = GNUNET_SCHEDULER_add_after (GNUNET_SCHEDULER_NO_TASK, &task2, cls);
+ /* t4 will go after t2 ('add after') and after t3 (priority) */
+ t4 = GNUNET_SCHEDULER_add_after (t2, &task4, cls);
+ /* t5 will go last (after p4) */
+ GNUNET_SCHEDULER_add_after (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
+taskShutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ int *ok = cls;
+
+ GNUNET_assert (1 == *ok);
+ *ok = 8;
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &taskLast, cls);
+ GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+checkShutdown ()
+{
+ int ok;
+
+ ok = 1;
+ GNUNET_SCHEDULER_run (&taskShutdown, &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_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &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 (GNUNET_SCHEDULER_add_after
+ (GNUNET_SCHEDULER_NO_TASK, &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 ();
+#ifndef MINGW
+ ret += checkSignal ();
+#endif
+ ret += checkShutdown ();
+ ret += checkCancel ();
+ GNUNET_DISK_pipe_close (p);
+
+ 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 0000000..8ba35f5
--- /dev/null
+++ b/src/util/test_scheduler_delay.c
@@ -0,0 +1,103 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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.abs_value > target.abs_value)
+ cumDelta += (now.abs_value - target.abs_value);
+ else
+ cumDelta += (target.abs_value - now.abs_value);
+ target =
+ GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, i));
+ FPRINTF (stderr, "%s", ".");
+ if (i > MAXV)
+ {
+ FPRINTF (stderr, "%s", "\n");
+ return;
+ }
+ GNUNET_SCHEDULER_add_delayed (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, "%s", "Timer precision is excellent.\n");
+ else if (cumDelta <= 50 * MAXV / INCR) /* 50 ms average deviation */
+ FPRINTF (stdout, "%s", "Timer precision is good.\n");
+ else if (cumDelta > 250 * MAXV / INCR)
+ FPRINTF (stdout, "%s", "Timer precision is awful.\n");
+ else
+ FPRINTF (stdout, "%s", "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 0000000..6718c65
--- /dev/null
+++ b/src/util/test_server.c
@@ -0,0 +1,222 @@
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_client_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2)
+
+#define MY_TYPE 128
+#define MY_TYPE2 129
+
+static struct GNUNET_SERVER_Handle *server;
+
+static struct GNUNET_CLIENT_Connection *cc;
+
+static struct GNUNET_SERVER_Client *argclient;
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+static int ok;
+
+
+static void
+finish_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_assert (ok == 6);
+ ok = 0;
+ GNUNET_SERVER_destroy (server);
+ GNUNET_CLIENT_disconnect (cc, GNUNET_NO);
+ GNUNET_CONFIGURATION_destroy (cfg);
+}
+
+
+static void
+recv_fin_cb (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ GNUNET_assert (ok == 5);
+ ok = 6;
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+ GNUNET_SCHEDULER_add_now (&finish_up, NULL);
+}
+
+
+static void
+first_reply_handler (void *cls, const struct GNUNET_MessageHeader *msg)
+{
+ GNUNET_assert (ok == 4);
+ ok = 5;
+ GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
+ GNUNET_SERVER_client_drop (argclient);
+ argclient = NULL;
+}
+
+
+static size_t
+reply_msg (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_MessageHeader msg;
+
+ GNUNET_assert (ok == 3);
+ ok = 4;
+ GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
+ msg.type = htons (MY_TYPE);
+ msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ memcpy (buf, &msg, sizeof (struct GNUNET_MessageHeader));
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+static void
+recv_cb (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ GNUNET_assert (ok == 2);
+ ok = 3;
+ argclient = client;
+ GNUNET_SERVER_client_keep (argclient);
+ GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (message->size));
+ GNUNET_assert (MY_TYPE == ntohs (message->type));
+ GNUNET_assert (NULL !=
+ GNUNET_SERVER_notify_transmit_ready (client,
+ ntohs (message->size),
+ TIMEOUT, &reply_msg,
+ NULL));
+}
+
+
+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 size_t
+transmit_second_message (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_MessageHeader msg;
+
+ GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
+ msg.type = htons (MY_TYPE2);
+ msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ memcpy (buf, &msg, sizeof (struct GNUNET_MessageHeader));
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+static size_t
+transmit_initial_message (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_MessageHeader msg;
+
+ GNUNET_assert (ok == 1);
+ ok = 2;
+ GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
+ msg.type = htons (MY_TYPE);
+ msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ memcpy (buf, &msg, sizeof (struct GNUNET_MessageHeader));
+ GNUNET_assert (NULL !=
+ GNUNET_CLIENT_notify_transmit_ready (cc,
+ sizeof (struct
+ GNUNET_MessageHeader),
+ TIMEOUT, GNUNET_YES,
+ &transmit_second_message,
+ NULL));
+ GNUNET_CLIENT_receive (cc, &first_reply_handler, NULL, TIMEOUT);
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+static void
+task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct sockaddr_in sa;
+ struct sockaddr *sap[2];
+ socklen_t slens[2];
+
+ sap[0] = (struct sockaddr *) &sa;
+ slens[0] = sizeof (sa);
+ sap[1] = NULL;
+ slens[1] = 0;
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ server = GNUNET_SERVER_create (NULL, NULL, sap, slens, TIMEOUT, GNUNET_NO);
+ GNUNET_assert (server != NULL);
+ GNUNET_SERVER_add_handlers (server, handlers);
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_number (cfg, "test-server", "PORT", PORT);
+ GNUNET_CONFIGURATION_set_value_string (cfg, "test-server", "HOSTNAME",
+ "localhost");
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ cc = GNUNET_CLIENT_connect ("test-server", cfg);
+ GNUNET_assert (cc != NULL);
+ GNUNET_assert (NULL !=
+ GNUNET_CLIENT_notify_transmit_ready (cc,
+ sizeof (struct
+ GNUNET_MessageHeader),
+ TIMEOUT, GNUNET_YES,
+ &transmit_initial_message,
+ NULL));
+}
+
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+check ()
+{
+ 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 0000000..8010695
--- /dev/null
+++ b/src/util/test_server_disconnect.c
@@ -0,0 +1,180 @@
+/*
+ This file is part of GNUnet.
+ (C) 2009, 2010 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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, specifically GNUNET_SERVER_client_disconnect
+ */
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_client_lib.h"
+#include "gnunet_scheduler_lib.h"
+#include "gnunet_server_lib.h"
+#include "gnunet_time_lib.h"
+
+#define VERBOSE GNUNET_NO
+
+#define PORT 12435
+
+#define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 250)
+
+#define MY_TYPE 128
+
+static struct GNUNET_SERVER_Handle *server;
+
+static struct GNUNET_CLIENT_Connection *cc;
+
+static struct GNUNET_CONFIGURATION_Handle *cfg;
+
+static int ok;
+
+
+static void
+finish_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_assert (ok == 5);
+ ok = 0;
+ GNUNET_SERVER_destroy (server);
+ GNUNET_CLIENT_disconnect (cc, GNUNET_NO);
+ GNUNET_CONFIGURATION_destroy (cfg);
+}
+
+
+static void
+notify_disconnect (void *cls, struct GNUNET_SERVER_Client *clientarg)
+{
+ if (clientarg == NULL)
+ return;
+ GNUNET_assert (ok == 4);
+ ok = 5;
+ GNUNET_SCHEDULER_add_now (&finish_up, NULL);
+}
+
+
+static void
+server_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct GNUNET_SERVER_Client *argclient = cls;
+
+ GNUNET_assert (ok == 3);
+ ok = 4;
+ GNUNET_SERVER_client_disconnect (argclient);
+ GNUNET_SERVER_client_drop (argclient);
+}
+
+
+static void
+recv_cb (void *cls, struct GNUNET_SERVER_Client *client,
+ const struct GNUNET_MessageHeader *message)
+{
+ GNUNET_assert (ok == 2);
+ ok = 3;
+ GNUNET_SERVER_client_keep (client);
+ GNUNET_SCHEDULER_add_now (&server_disconnect, client);
+ GNUNET_assert (sizeof (struct GNUNET_MessageHeader) == ntohs (message->size));
+ GNUNET_assert (MY_TYPE == ntohs (message->type));
+ GNUNET_SERVER_receive_done (client, GNUNET_OK);
+}
+
+
+static struct GNUNET_SERVER_MessageHandler handlers[] = {
+ {&recv_cb, NULL, MY_TYPE, sizeof (struct GNUNET_MessageHeader)},
+ {NULL, NULL, 0, 0}
+};
+
+
+static size_t
+transmit_initial_message (void *cls, size_t size, void *buf)
+{
+ struct GNUNET_MessageHeader msg;
+
+ GNUNET_assert (ok == 1);
+ ok = 2;
+ GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader));
+ msg.type = htons (MY_TYPE);
+ msg.size = htons (sizeof (struct GNUNET_MessageHeader));
+ memcpy (buf, &msg, sizeof (struct GNUNET_MessageHeader));
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+static void
+task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ struct sockaddr_in sa;
+ struct sockaddr *sap[2];
+ socklen_t slens[2];
+
+ sap[0] = (struct sockaddr *) &sa;
+ slens[0] = sizeof (sa);
+ sap[1] = NULL;
+ slens[1] = 0;
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ server = GNUNET_SERVER_create (NULL, NULL, sap, slens, TIMEOUT, GNUNET_NO);
+ GNUNET_assert (server != NULL);
+ GNUNET_SERVER_add_handlers (server, handlers);
+ GNUNET_SERVER_disconnect_notify (server, &notify_disconnect, NULL);
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_number (cfg, "test-server", "PORT", PORT);
+ GNUNET_CONFIGURATION_set_value_string (cfg, "test-server", "HOSTNAME",
+ "localhost");
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ cc = GNUNET_CLIENT_connect ("test-server", cfg);
+ GNUNET_assert (cc != NULL);
+ GNUNET_assert (NULL !=
+ GNUNET_CLIENT_notify_transmit_ready (cc,
+ sizeof (struct
+ GNUNET_MessageHeader),
+ TIMEOUT, GNUNET_YES,
+ &transmit_initial_message,
+ NULL));
+}
+
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+check ()
+{
+ ok = 1;
+ GNUNET_SCHEDULER_run (&task, &ok);
+ return ok;
+}
+
+
+int
+main (int argc, char *argv[])
+{
+ int ret = 0;
+
+ GNUNET_log_setup ("test_server_disconnect", "WARNING", 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 0000000..06a4b71
--- /dev/null
+++ b/src/util/test_server_with_client.c
@@ -0,0 +1,224 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_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_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));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ 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 (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, 50),
+ &send_done, argclient);
+ break;
+ case 4:
+ ok++;
+ GNUNET_CLIENT_disconnect (client, GNUNET_YES);
+ GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+
+}
+
+
+static void
+clean_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_SERVER_destroy (server);
+ server = NULL;
+ GNUNET_CONFIGURATION_destroy (cfg);
+ cfg = NULL;
+}
+
+
+/**
+ * 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)
+{
+ if (client == NULL)
+ return;
+ GNUNET_assert (ok == 5);
+ ok = 0;
+ GNUNET_SCHEDULER_add_now (&clean_up, NULL);
+}
+
+
+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;
+ struct sockaddr *sap[2];
+ socklen_t slens[2];
+
+ sap[0] = (struct sockaddr *) &sa;
+ slens[0] = sizeof (sa);
+ sap[1] = NULL;
+ slens[1] = 0;
+ memset (&sa, 0, sizeof (sa));
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ sa.sin_len = sizeof (sa);
+#endif
+ sa.sin_family = AF_INET;
+ sa.sin_port = htons (PORT);
+ server =
+ GNUNET_SERVER_create (NULL, NULL, sap, slens,
+ 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, &notify_disconnect, cls);
+ cfg = GNUNET_CONFIGURATION_create ();
+ GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT);
+ GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", "localhost");
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+ client = GNUNET_CLIENT_connect ("test", cfg);
+ GNUNET_assert (client != NULL);
+ GNUNET_CLIENT_notify_transmit_ready (client, 256,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, 250),
+ GNUNET_NO, &notify_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_server_with_client_unix.c b/src/util/test_server_with_client_unix.c
new file mode 100644
index 0000000..99af4e8
--- /dev/null
+++ b/src/util/test_server_with_client_unix.c
@@ -0,0 +1,212 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_unix.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 MY_TYPE 128
+
+
+static struct GNUNET_SERVER_Handle *server;
+
+static struct GNUNET_CLIENT_Connection *client;
+
+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_Client *argclient,
+ const struct GNUNET_MessageHeader *message)
+{
+ switch (ok)
+ {
+ case 2:
+ ok++;
+ GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, 50),
+ &send_done, argclient);
+ break;
+ case 4:
+ ok++;
+ GNUNET_CLIENT_disconnect (client, GNUNET_YES);
+ GNUNET_SERVER_receive_done (argclient, GNUNET_OK);
+ break;
+ default:
+ GNUNET_assert (0);
+ }
+
+}
+
+
+static void
+clean_up (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_SERVER_destroy (server);
+ server = NULL;
+ GNUNET_CONFIGURATION_destroy (cfg);
+ cfg = NULL;
+}
+
+
+/**
+ * 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)
+{
+ if (client == NULL)
+ return;
+ GNUNET_assert (ok == 5);
+ ok = 0;
+ GNUNET_SCHEDULER_add_now (&clean_up, NULL);
+}
+
+
+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_un un;
+ const char *unixpath = "/tmp/testsock";
+ size_t slen = strlen (unixpath);
+ struct sockaddr *sap[2];
+ socklen_t slens[2];
+
+ memset (&un, 0, sizeof (un));
+ un.sun_family = AF_UNIX;
+ memcpy (un.sun_path, unixpath, slen);
+ un.sun_path[slen] = '\0';
+#if HAVE_SOCKADDR_IN_SIN_LEN
+ un.sun_len = (u_char) sizeof (un);
+#endif
+#if LINUX
+ un.sun_path[0] = '\0';
+#endif
+
+
+ sap[0] = (struct sockaddr *) &un;
+ slens[0] = sizeof (un);
+ sap[1] = NULL;
+ slens[1] = 0;
+ server =
+ GNUNET_SERVER_create (NULL, NULL, sap, slens,
+ 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, &notify_disconnect, cls);
+ cfg = GNUNET_CONFIGURATION_create ();
+
+ GNUNET_CONFIGURATION_set_value_string (cfg, "test", "UNIXPATH", unixpath);
+ GNUNET_CONFIGURATION_set_value_string (cfg, "resolver", "HOSTNAME",
+ "localhost");
+
+ client = GNUNET_CLIENT_connect ("test", cfg);
+ GNUNET_assert (client != NULL);
+ GNUNET_CLIENT_notify_transmit_ready (client, 256,
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_MILLISECONDS, 250),
+ GNUNET_NO, &notify_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_unix",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check ();
+
+ return ret;
+}
+
+/* end of test_server_with_client_unix.c */
diff --git a/src/util/test_service.c b/src/util/test_service.c
new file mode 100644
index 0000000..049282d
--- /dev/null
+++ b/src/util/test_service.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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_SERVICE_Context *sctx;
+
+static int ok = 1;
+
+
+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 (struct GNUNET_MessageHeader));
+ GNUNET_CLIENT_disconnect (client, GNUNET_NO);
+ return sizeof (struct GNUNET_MessageHeader);
+}
+
+
+static void
+ready (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct GNUNET_CLIENT_Connection *client;
+
+ GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service confirmed running\n");
+ client = GNUNET_CLIENT_connect ("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, GNUNET_NO,
+ &build_msg, client);
+}
+
+
+static void
+do_stop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ GNUNET_SERVICE_stop (sctx);
+}
+
+
+static void
+recv_cb (void *cls, 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);
+ if (sctx != NULL)
+ GNUNET_SCHEDULER_add_now (&do_stop, NULL);
+ else
+ GNUNET_SCHEDULER_shutdown ();
+ ok = 0;
+}
+
+
+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_SERVER_Handle *server,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
+{
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service initializing\n");
+ GNUNET_SERVER_add_handlers (server, myhandlers);
+ GNUNET_CLIENT_service_test ("test_service", cfg, GNUNET_TIME_UNIT_SECONDS,
+ &ready, (void *) cfg);
+}
+
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+check ()
+{
+ 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",
+ GNUNET_SERVICE_OPTION_NONE, &runner, &ok));
+ GNUNET_assert (0 == ok);
+ return ok;
+}
+
+static void
+ready6 (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ struct GNUNET_CLIENT_Connection *client;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "V6 ready\n");
+ GNUNET_assert (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE));
+ client = GNUNET_CLIENT_connect ("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, GNUNET_NO,
+ &build_msg, client);
+}
+
+static void
+runner6 (void *cls, struct GNUNET_SERVER_Handle *server,
+ const 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 ("test_service6", cfg, GNUNET_TIME_UNIT_SECONDS,
+ &ready6, (void *) cfg);
+}
+
+/**
+ * Main method, starts scheduler with task1,
+ * checks that "ok" is correct at the end.
+ */
+static int
+check6 ()
+{
+ 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",
+ GNUNET_SERVICE_OPTION_NONE, &runner6,
+ &ok));
+ GNUNET_assert (0 == ok);
+ return ok;
+}
+
+
+
+static void
+start_stop_main (void *cls, char *const *args, const char *cfgfile,
+ const 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", cfg);
+ GNUNET_assert (NULL != sctx);
+ runner (cls, 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;
+ struct GNUNET_NETWORK_Handle *s = NULL;
+
+ GNUNET_log_setup ("test-service",
+#if VERBOSE
+ "DEBUG",
+#else
+ "WARNING",
+#endif
+ NULL);
+ ret += check ();
+ ret += check ();
+
+ // FIXME
+#ifndef MINGW
+ s = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0);
+#endif
+ if (NULL == s)
+ {
+ 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 (GNUNET_OK == GNUNET_NETWORK_socket_close (s));
+ ret += check6 ();
+ }
+ 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 0000000..7f6baaa
--- /dev/null
+++ b/src/util/test_service_data.conf
@@ -0,0 +1,29 @@
+[test_service]
+PORT=12435
+BINDTO=localhost
+PIDFILE=/tmp/test-service.pid
+TIMEOUT=30 s
+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
+
+[test_service6]
+PORT=12435
+PIDFILE=/tmp/test-service.pid
+TIMEOUT=30 s
+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
+
+[resolver]
+HOSTNAME=localhost
+
+
diff --git a/src/util/test_strings.c b/src/util/test_strings.c
new file mode 100644
index 0000000..570776a
--- /dev/null
+++ b/src/util/test_strings.c
@@ -0,0 +1,117 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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;
+ const char *hdir;
+
+ 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);
+#ifndef MINGW
+ hdir = getenv ("HOME");
+#else
+ hdir = getenv ("USERPROFILE");
+#endif
+ GNUNET_snprintf (buf, sizeof (buf), "%s%s", hdir, DIR_SEPARATOR_STR);
+ b = GNUNET_STRINGS_filename_expand ("~");
+ GNUNET_assert (b != NULL);
+ 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.abs_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;
+ }
+ GNUNET_free (r);
+ b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "ASCII");
+ WANT ("TEST", b);
+ GNUNET_log_skip (2, GNUNET_NO);
+ b = GNUNET_STRINGS_to_utf8 ("TEST", 4, "unknown");
+ GNUNET_log_skip (0, GNUNET_YES);
+ WANT ("TEST", b);
+ 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 0000000..788884f
--- /dev/null
+++ b/src/util/test_time.c
@@ -0,0 +1,244 @@
+/*
+ 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 3, or (at your
+ option) any later version.
+
+ GNUnet is distributed in the hope that 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_Absolute forever;
+ struct GNUNET_TIME_Absolute zero;
+ struct GNUNET_TIME_Relative rel;
+ struct GNUNET_TIME_Relative relForever;
+ struct GNUNET_TIME_Relative relUnit;
+ struct GNUNET_TIME_RelativeNBO reln;
+ unsigned int i;
+
+ forever = GNUNET_TIME_absolute_get_forever ();
+ relForever = GNUNET_TIME_relative_get_forever ();
+ relUnit = GNUNET_TIME_relative_get_unit ();
+ zero.abs_value = 0;
+
+ last = now = GNUNET_TIME_absolute_get ();
+ while (now.abs_value == last.abs_value)
+ now = GNUNET_TIME_absolute_get ();
+ GNUNET_assert (now.abs_value > last.abs_value);
+
+ /* test overflow checking in multiply */
+ rel = GNUNET_TIME_UNIT_SECONDS;
+ GNUNET_log_skip (1, GNUNET_NO);
+ for (i = 0; i < 55; i++)
+ rel = GNUNET_TIME_relative_multiply (rel, 2);
+ GNUNET_log_skip (0, GNUNET_NO);
+ GNUNET_assert (rel.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value);
+ /*check zero */
+ rel.rel_value = (UINT64_MAX) - 1024;
+ GNUNET_assert (GNUNET_TIME_relative_get_zero ().rel_value ==
+ GNUNET_TIME_relative_multiply (rel, 0).rel_value);
+
+ /* test infinity-check for relative to absolute */
+ GNUNET_log_skip (1, GNUNET_NO);
+ last = GNUNET_TIME_relative_to_absolute (rel);
+ GNUNET_assert (last.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value);
+ GNUNET_log_skip (0, GNUNET_YES);
+
+ /*check relative to absolute */
+ rel.rel_value = 0;
+ GNUNET_assert (GNUNET_TIME_absolute_get ().abs_value ==
+ GNUNET_TIME_relative_to_absolute (rel).abs_value);
+ /*check forever */
+ rel.rel_value = UINT64_MAX;
+ GNUNET_assert (GNUNET_TIME_absolute_get_forever ().abs_value ==
+ GNUNET_TIME_relative_to_absolute (rel).abs_value);
+ /* check overflow for r2a */
+ rel.rel_value = (UINT64_MAX) - 1024;
+ GNUNET_log_skip (1, GNUNET_NO);
+ last = GNUNET_TIME_relative_to_absolute (rel);
+ GNUNET_log_skip (0, GNUNET_NO);
+ GNUNET_assert (last.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value);
+
+ /* check overflow for relative add */
+ GNUNET_log_skip (1, GNUNET_NO);
+ rel = GNUNET_TIME_relative_add (rel, rel);
+ GNUNET_log_skip (0, GNUNET_NO);
+ GNUNET_assert (rel.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value);
+
+ GNUNET_log_skip (1, GNUNET_NO);
+ rel = GNUNET_TIME_relative_add (relForever, relForever);
+ GNUNET_log_skip (0, GNUNET_NO);
+ GNUNET_assert (rel.rel_value == relForever.rel_value);
+
+ GNUNET_log_skip (1, GNUNET_NO);
+ rel = GNUNET_TIME_relative_add (relUnit, relUnit);
+ GNUNET_assert (rel.rel_value == 2 * relUnit.rel_value);
+
+ /* check relation check in get_duration */
+ future.abs_value = now.abs_value + 1000000;
+ GNUNET_assert (GNUNET_TIME_absolute_get_difference (now, future).rel_value ==
+ 1000000);
+ GNUNET_assert (GNUNET_TIME_absolute_get_difference (future, now).rel_value ==
+ 0);
+
+ GNUNET_assert (GNUNET_TIME_absolute_get_difference (zero, forever).rel_value
+ == forever.abs_value);
+
+ past.abs_value = now.abs_value - 1000000;
+ rel = GNUNET_TIME_absolute_get_duration (future);
+ GNUNET_assert (rel.rel_value == 0);
+ rel = GNUNET_TIME_absolute_get_duration (past);
+ GNUNET_assert (rel.rel_value >= 1000000);
+
+ /* check get remaining */
+ rel = GNUNET_TIME_absolute_get_remaining (now);
+ GNUNET_assert (rel.rel_value == 0);
+ rel = GNUNET_TIME_absolute_get_remaining (past);
+ GNUNET_assert (rel.rel_value == 0);
+ rel = GNUNET_TIME_absolute_get_remaining (future);
+ GNUNET_assert (rel.rel_value > 0);
+ GNUNET_assert (rel.rel_value <= 1000000);
+ forever = GNUNET_TIME_absolute_get_forever ();
+ GNUNET_assert (GNUNET_TIME_relative_get_forever ().rel_value ==
+ GNUNET_TIME_absolute_get_remaining (forever).rel_value);
+
+ /* check endianess */
+ reln = GNUNET_TIME_relative_hton (rel);
+ GNUNET_assert (rel.rel_value == GNUNET_TIME_relative_ntoh (reln).rel_value);
+ nown = GNUNET_TIME_absolute_hton (now);
+ GNUNET_assert (now.abs_value == GNUNET_TIME_absolute_ntoh (nown).abs_value);
+
+ /* check absolute addition */
+ future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_SECONDS);
+ GNUNET_assert (future.abs_value == now.abs_value + 1000);
+
+ future = GNUNET_TIME_absolute_add (forever, GNUNET_TIME_UNIT_ZERO);
+ GNUNET_assert (future.abs_value == forever.abs_value);
+
+ rel.rel_value = (UINT64_MAX) - 1024;
+ now.abs_value = rel.rel_value;
+ future = GNUNET_TIME_absolute_add (now, rel);
+ GNUNET_assert (future.abs_value == forever.abs_value);
+
+ /* check zero */
+ future = GNUNET_TIME_absolute_add (now, GNUNET_TIME_UNIT_ZERO);
+ GNUNET_assert (future.abs_value == now.abs_value);
+
+ GNUNET_assert (forever.abs_value ==
+ GNUNET_TIME_absolute_subtract (forever,
+ GNUNET_TIME_UNIT_MINUTES).abs_value);
+ /*check absolute subtract */
+ now.abs_value = 50000;
+ rel.rel_value = 100000;
+ GNUNET_assert (GNUNET_TIME_UNIT_ZERO_ABS.abs_value ==
+ (GNUNET_TIME_absolute_subtract (now, rel)).abs_value);
+ rel.rel_value = 10000;
+ GNUNET_assert (40000 == (GNUNET_TIME_absolute_subtract (now, rel)).abs_value);
+
+ /*check relative divide */
+ GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value ==
+ (GNUNET_TIME_relative_divide (rel, 0)).rel_value);
+
+ rel = GNUNET_TIME_UNIT_FOREVER_REL;
+ GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value ==
+ (GNUNET_TIME_relative_divide (rel, 2)).rel_value);
+
+ rel = GNUNET_TIME_relative_divide (relUnit, 2);
+ GNUNET_assert (rel.rel_value == relUnit.rel_value / 2);
+
+
+ /* check Return absolute time of 0ms */
+ zero = GNUNET_TIME_absolute_get_zero ();
+
+ /* check GNUNET_TIME_calculate_eta */
+ last.abs_value = GNUNET_TIME_absolute_get ().abs_value - 1024;
+ forever = GNUNET_TIME_absolute_get_forever ();
+ forever.abs_value = forever.abs_value - 1024;
+ GNUNET_assert (GNUNET_TIME_absolute_get_zero ().abs_value ==
+ GNUNET_TIME_calculate_eta (forever, 50000, 100000).rel_value);
+ /* check zero */
+ GNUNET_log_skip (1, GNUNET_NO);
+ GNUNET_assert (GNUNET_TIME_UNIT_ZERO.rel_value ==
+ (GNUNET_TIME_calculate_eta (last, 60000, 50000)).rel_value);
+ GNUNET_log_skip (0, GNUNET_YES);
+ /*check forever */
+ GNUNET_assert (GNUNET_TIME_UNIT_FOREVER_REL.rel_value ==
+ (GNUNET_TIME_calculate_eta (last, 0, 50000)).rel_value);
+
+ /*check relative subtract */
+ now = GNUNET_TIME_absolute_get ();
+ rel.rel_value = now.abs_value;
+ relForever.rel_value = rel.rel_value + 1024;
+ GNUNET_assert (1024 ==
+ GNUNET_TIME_relative_subtract (relForever, rel).rel_value);
+ /*check zero */
+ GNUNET_assert (GNUNET_TIME_relative_get_zero ().rel_value ==
+ GNUNET_TIME_relative_subtract (rel, relForever).rel_value);
+ /*check forever */
+ rel.rel_value = UINT64_MAX;
+ GNUNET_assert (GNUNET_TIME_relative_get_forever ().rel_value ==
+ GNUNET_TIME_relative_subtract (rel, relForever).rel_value);
+
+ /*check GNUNET_TIME_relative_min */
+ now = GNUNET_TIME_absolute_get ();
+ rel.rel_value = now.abs_value;
+ relForever.rel_value = rel.rel_value - 1024;
+ GNUNET_assert (relForever.rel_value ==
+ GNUNET_TIME_relative_min (rel, relForever).rel_value);
+
+ /*check GNUNET_TIME_relative_max */
+ GNUNET_assert (rel.rel_value ==
+ GNUNET_TIME_relative_max (rel, relForever).rel_value);
+
+ /*check GNUNET_TIME_absolute_min */
+ now = GNUNET_TIME_absolute_get ();
+ last.abs_value = now.abs_value - 1024;
+ GNUNET_assert (last.abs_value ==
+ GNUNET_TIME_absolute_min (now, last).abs_value);
+
+ /*check GNUNET_TIME_absolute_max */
+ GNUNET_assert (now.abs_value ==
+ GNUNET_TIME_absolute_max (now, last).abs_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 0000000..c57ccd1
--- /dev/null
+++ b/src/util/time.c
@@ -0,0 +1,521 @@
+/*
+ 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"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Variable used to simulate clock skew. Used for testing, never in production.
+ */
+static long long timestamp_offset;
+
+/**
+ * Set the timestamp offset for this instance.
+ *
+ * @param offset the offset to skew the locale time by
+ */
+void
+GNUNET_TIME_set_offset (long long offset)
+{
+ timestamp_offset = offset;
+}
+
+/**
+ * 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.abs_value =
+ (uint64_t) (((uint64_t) tv.tv_sec * 1000LL) +
+ ((uint64_t) tv.tv_usec / 1000LL)) + timestamp_offset;
+ 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 absolute time of 0ms.
+ */
+struct GNUNET_TIME_Absolute
+GNUNET_TIME_absolute_get_zero ()
+{
+ static struct GNUNET_TIME_Absolute 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_MAX };
+ return forever;
+}
+
+/**
+ * Return "forever".
+ */
+struct GNUNET_TIME_Absolute
+GNUNET_TIME_absolute_get_forever ()
+{
+ static struct GNUNET_TIME_Absolute forever = { UINT64_MAX };
+ 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.rel_value == UINT64_MAX)
+ return GNUNET_TIME_absolute_get_forever ();
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+
+ if (rel.rel_value + now.abs_value < rel.rel_value)
+ {
+ GNUNET_break (0); /* overflow... */
+ return GNUNET_TIME_absolute_get_forever ();
+ }
+ ret.abs_value = rel.rel_value + now.abs_value;
+ return ret;
+}
+
+
+/**
+ * Return the minimum of two relative time values.
+ *
+ * @param t1 first timestamp
+ * @param t2 other timestamp
+ * @return timestamp that is smaller
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_min (struct GNUNET_TIME_Relative t1,
+ struct GNUNET_TIME_Relative t2)
+{
+ return (t1.rel_value < t2.rel_value) ? t1 : t2;
+}
+
+
+/**
+ * Return the maximum of two relative time values.
+ *
+ * @param t1 first timestamp
+ * @param t2 other timestamp
+ * @return timestamp that is larger
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_max (struct GNUNET_TIME_Relative t1,
+ struct GNUNET_TIME_Relative t2)
+{
+ return (t1.rel_value > t2.rel_value) ? t1 : t2;
+}
+
+
+
+/**
+ * Return the minimum of two relative time values.
+ *
+ * @param t1 first timestamp
+ * @param t2 other timestamp
+ * @return timestamp that is smaller
+ */
+struct GNUNET_TIME_Absolute
+GNUNET_TIME_absolute_min (struct GNUNET_TIME_Absolute t1,
+ struct GNUNET_TIME_Absolute t2)
+{
+ return (t1.abs_value < t2.abs_value) ? t1 : t2;
+}
+
+
+/**
+ * Return the maximum of two relative time values.
+ *
+ * @param t1 first timestamp
+ * @param t2 other timestamp
+ * @return timestamp that is smaller
+ */
+struct GNUNET_TIME_Absolute
+GNUNET_TIME_absolute_max (struct GNUNET_TIME_Absolute t1,
+ struct GNUNET_TIME_Absolute t2)
+{
+ return (t1.abs_value > t2.abs_value) ? t1 : t2;
+}
+
+
+/**
+ * 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.abs_value == UINT64_MAX)
+ return GNUNET_TIME_relative_get_forever ();
+ struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
+
+ if (now.abs_value > future.abs_value)
+ return GNUNET_TIME_relative_get_zero ();
+ ret.rel_value = future.abs_value - now.abs_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 handled 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.abs_value == UINT64_MAX)
+ return GNUNET_TIME_relative_get_forever ();
+ if (end.abs_value < start.abs_value)
+ return GNUNET_TIME_relative_get_zero ();
+ ret.rel_value = end.abs_value - start.abs_value;
+ return ret;
+}
+
+/**
+ * Get the duration of an operation as the
+ * difference of the current time and the given start time "whence".
+ *
+ * @return aborts if whence==FOREVER, 0 if whence > now, otherwise now-whence.
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_absolute_get_duration (struct GNUNET_TIME_Absolute whence)
+{
+ struct GNUNET_TIME_Absolute now;
+ struct GNUNET_TIME_Relative ret;
+
+ now = GNUNET_TIME_absolute_get ();
+ GNUNET_assert (whence.abs_value != UINT64_MAX);
+ if (whence.abs_value > now.abs_value)
+ return GNUNET_TIME_relative_get_zero ();
+ ret.rel_value = now.abs_value - whence.abs_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.abs_value == UINT64_MAX) || (duration.rel_value == UINT64_MAX))
+ return GNUNET_TIME_absolute_get_forever ();
+ if (start.abs_value + duration.rel_value < start.abs_value)
+ {
+ GNUNET_break (0);
+ return GNUNET_TIME_absolute_get_forever ();
+ }
+ ret.abs_value = start.abs_value + duration.rel_value;
+ return ret;
+}
+
+
+/**
+ * Subtract a given relative duration from the
+ * given start time.
+ *
+ * @param start some absolute time
+ * @param duration some relative time to subtract
+ * @return ZERO if start <= duration, or FOREVER if start time is FOREVER; start-duration otherwise
+ */
+struct GNUNET_TIME_Absolute
+GNUNET_TIME_absolute_subtract (struct GNUNET_TIME_Absolute start,
+ struct GNUNET_TIME_Relative duration)
+{
+ struct GNUNET_TIME_Absolute ret;
+
+ if (start.abs_value <= duration.rel_value)
+ return GNUNET_TIME_UNIT_ZERO_ABS;
+ if (start.abs_value == GNUNET_TIME_UNIT_FOREVER_ABS.abs_value)
+ return GNUNET_TIME_UNIT_FOREVER_ABS;
+ ret.abs_value = start.abs_value - duration.rel_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.rel_value = rel.rel_value * (unsigned long long) factor;
+ if (ret.rel_value / factor != rel.rel_value)
+ {
+ GNUNET_break (0);
+ return GNUNET_TIME_relative_get_forever ();
+ }
+ return ret;
+}
+
+
+/**
+ * Divide relative time by a given factor.
+ *
+ * @param rel some duration
+ * @param factor integer to divide by
+ * @return FOREVER if rel=FOREVER or factor==0; otherwise rel/factor
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_divide (struct GNUNET_TIME_Relative rel,
+ unsigned int factor)
+{
+ struct GNUNET_TIME_Relative ret;
+
+ if ((factor == 0) ||
+ (rel.rel_value == GNUNET_TIME_UNIT_FOREVER_REL.rel_value))
+ return GNUNET_TIME_UNIT_FOREVER_REL;
+ ret.rel_value = rel.rel_value / (unsigned long long) factor;
+ return ret;
+}
+
+
+/**
+ * Calculate the estimate time of arrival/completion
+ * for an operation.
+ *
+ * @param start when did the operation start?
+ * @param finished how much has been done?
+ * @param total how much must be done overall (same unit as for "finished")
+ * @return remaining duration for the operation,
+ * assuming it continues at the same speed
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_calculate_eta (struct GNUNET_TIME_Absolute start, uint64_t finished,
+ uint64_t total)
+{
+ struct GNUNET_TIME_Relative dur;
+ double exp;
+ struct GNUNET_TIME_Relative ret;
+
+ GNUNET_break (finished <= total);
+ if (finished >= total)
+ return GNUNET_TIME_UNIT_ZERO;
+ if (finished == 0)
+ return GNUNET_TIME_UNIT_FOREVER_REL;
+ dur = GNUNET_TIME_absolute_get_duration (start);
+ exp = ((double) dur.rel_value) * ((double) total) / ((double) finished);
+ ret.rel_value = ((uint64_t) exp) - dur.rel_value;
+ return ret;
+}
+
+
+/**
+ * Add relative times together.
+ *
+ * @param a1 first timestamp
+ * @param a2 second timestamp
+ * @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.rel_value == UINT64_MAX) || (a2.rel_value == UINT64_MAX))
+ return GNUNET_TIME_relative_get_forever ();
+ if (a1.rel_value + a2.rel_value < a1.rel_value)
+ {
+ GNUNET_break (0);
+ return GNUNET_TIME_relative_get_forever ();
+ }
+ ret.rel_value = a1.rel_value + a2.rel_value;
+ return ret;
+}
+
+
+/**
+ * Subtract relative timestamp from the other.
+ *
+ * @param a1 first timestamp
+ * @param a2 second timestamp
+ * @return ZERO if a2>=a1 (including both FOREVER), FOREVER if a1 is FOREVER, a1-a2 otherwise
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_subtract (struct GNUNET_TIME_Relative a1,
+ struct GNUNET_TIME_Relative a2)
+{
+ struct GNUNET_TIME_Relative ret;
+
+ if (a2.rel_value >= a1.rel_value)
+ return GNUNET_TIME_relative_get_zero ();
+ if (a1.rel_value == UINT64_MAX)
+ return GNUNET_TIME_relative_get_forever ();
+ ret.rel_value = a1.rel_value - a2.rel_value;
+ return ret;
+}
+
+
+/**
+ * Convert relative time to network byte order.
+ *
+ * @param a time to convert
+ * @return time in network byte order
+ */
+struct GNUNET_TIME_RelativeNBO
+GNUNET_TIME_relative_hton (struct GNUNET_TIME_Relative a)
+{
+ struct GNUNET_TIME_RelativeNBO ret;
+
+ ret.rel_value__ = GNUNET_htonll (a.rel_value);
+ return ret;
+}
+
+/**
+ * Convert relative time from network byte order.
+ *
+ * @param a time to convert
+ * @return time in host byte order
+ */
+struct GNUNET_TIME_Relative
+GNUNET_TIME_relative_ntoh (struct GNUNET_TIME_RelativeNBO a)
+{
+ struct GNUNET_TIME_Relative ret;
+
+ ret.rel_value = GNUNET_ntohll (a.rel_value__);
+ return ret;
+
+}
+
+/**
+ * Convert absolute time to network byte order.
+ *
+ * @param a time to convert
+ * @return time in network byte order
+ */
+struct GNUNET_TIME_AbsoluteNBO
+GNUNET_TIME_absolute_hton (struct GNUNET_TIME_Absolute a)
+{
+ struct GNUNET_TIME_AbsoluteNBO ret;
+
+ ret.abs_value__ = GNUNET_htonll (a.abs_value);
+ return ret;
+}
+
+/**
+ * Convert absolute time from network byte order.
+ *
+ * @param a time to convert
+ * @return time in host byte order
+ */
+struct GNUNET_TIME_Absolute
+GNUNET_TIME_absolute_ntoh (struct GNUNET_TIME_AbsoluteNBO a)
+{
+ struct GNUNET_TIME_Absolute ret;
+
+ ret.abs_value = GNUNET_ntohll (a.abs_value__);
+ return ret;
+
+}
+
+/**
+ * Convert a relative time to a string.
+ * This is one of the very few calls in the entire API that is
+ * NOT reentrant!
+ *
+ * @param time the time to print
+ *
+ * @return string form of the time (as milliseconds)
+ */
+const char *
+GNUNET_TIME_relative_to_string (struct GNUNET_TIME_Relative time)
+{
+ static char time_string[21];
+
+ memset (time_string, 0, sizeof (time_string));
+
+ sprintf (time_string, "%llu", (unsigned long long) time.rel_value);
+ return (const char *) time_string;
+}
+
+
+
+/* end of time.c */
diff --git a/src/util/util.conf b/src/util/util.conf
new file mode 100644
index 0000000..ba9dfec
--- /dev/null
+++ b/src/util/util.conf
@@ -0,0 +1,16 @@
+[PATHS]
+SERVICEHOME = ~/.gnunet/
+# SERVICEHOME = /var/lib/gnunet/
+# DEFAULTCONFIG = /etc/gnunet.conf
+# If 'DEFAULTCONFIG' is not defined, the current
+# configuration file is assumed to be the default,
+# which is what we want by default...
+
+[gnunetd]
+HOSTKEY = $SERVICEHOME/.hostkey
+
+[client]
+HOME = $SERVICEHOME
+
+[TESTING]
+WEAKRANDOM = NO
diff --git a/src/util/win.cc b/src/util/win.cc
new file mode 100644
index 0000000..1f66072
--- /dev/null
+++ b/src/util/win.cc
@@ -0,0 +1,1266 @@
+/*
+ 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/win.cc
+ * @brief Helper functions for MS Windows in C++
+ * @author Nils Durner
+ */
+
+#ifndef _WIN_CC
+#define _WIN_CC
+
+#include "winproc.h"
+#include "platform.h"
+#include "gnunet_common.h"
+#include "gnunet_connection_lib.h"
+
+#include <list>
+using namespace std;
+#include <ntdef.h>
+
+#ifndef INHERITED_ACE
+#define INHERITED_ACE 0x10
+#endif
+
+extern "C" {
+
+int plibc_conv_to_win_path(const char *pszUnix, char *pszWindows);
+
+#define _IP_ADAPTER_UNICAST_ADDRESS_HEAD \
+ union { \
+ struct { \
+ ULONG Length; \
+ DWORD Flags; \
+ }; \
+ }; \
+
+#define _IP_ADAPTER_UNICAST_ADDRESS_BASE \
+ SOCKET_ADDRESS Address; \
+ IP_PREFIX_ORIGIN PrefixOrigin; \
+ IP_SUFFIX_ORIGIN SuffixOrigin; \
+ IP_DAD_STATE DadState; \
+ ULONG ValidLifetime; \
+ ULONG PreferredLifetime; \
+ ULONG LeaseLifetime;
+
+#define _IP_ADAPTER_UNICAST_ADDRESS_ADD_VISTA \
+ UINT8 OnLinkPrefixLength;
+
+
+#define _IP_ADAPTER_UNICAST_ADDRESS_DEFINE(suffix,addition) \
+typedef struct _IP_ADAPTER_UNICAST_ADDRESS##suffix { \
+ _IP_ADAPTER_UNICAST_ADDRESS_HEAD \
+ struct _IP_ADAPTER_UNICAST_ADDRESS##suffix *Next; \
+ _IP_ADAPTER_UNICAST_ADDRESS_BASE \
+ addition \
+} IP_ADAPTER_UNICAST_ADDRESS##suffix, *PIP_ADAPTER_UNICAST_ADDRESS##suffix;
+
+/* _IP_ADAPTER_UNICAST_ADDRESS_DEFINE(,) defined in w32api headers */
+_IP_ADAPTER_UNICAST_ADDRESS_DEFINE(_VISTA,_IP_ADAPTER_UNICAST_ADDRESS_ADD_VISTA)
+
+
+typedef struct _IP_ADAPTER_WINS_SERVER_ADDRESS {
+ union {
+ ULONGLONG Alignment;
+ struct {
+ ULONG Length;
+ DWORD Reserved;
+ };
+ };
+ struct _IP_ADAPTER_WINS_SERVER_ADDRESS *Next;
+ SOCKET_ADDRESS Address;
+} IP_ADAPTER_WINS_SERVER_ADDRESS, *PIP_ADAPTER_WINS_SERVER_ADDRESS, *PIP_ADAPTER_WINS_SERVER_ADDRESS_LH;
+
+typedef struct _IP_ADAPTER_GATEWAY_ADDRESS {
+ union {
+ ULONGLONG Alignment;
+ struct {
+ ULONG Length;
+ DWORD Reserved;
+ };
+ };
+ struct _IP_ADAPTER_GATEWAY_ADDRESS *Next;
+ SOCKET_ADDRESS Address;
+} IP_ADAPTER_GATEWAY_ADDRESS, *PIP_ADAPTER_GATEWAY_ADDRESS, *PIP_ADAPTER_GATEWAY_ADDRESS_LH;
+
+typedef UINT32 NET_IF_COMPARTMENT_ID;
+typedef GUID NET_IF_NETWORK_GUID;
+
+typedef enum _NET_IF_CONNECTION_TYPE {
+ NET_IF_CONNECTION_DEDICATED = 1,
+ NET_IF_CONNECTION_PASSIVE,
+ NET_IF_CONNECTION_DEMAND,
+ NET_IF_CONNECTION_MAXIMUM
+} NET_IF_CONNECTION_TYPE, *PNET_IF_CONNECTION_TYPE;
+
+typedef enum {
+ TUNNEL_TYPE_NONE = 0,
+ TUNNEL_TYPE_OTHER,
+ TUNNEL_TYPE_DIRECT,
+ TUNNEL_TYPE_6TO4,
+ TUNNEL_TYPE_ISATAP,
+ TUNNEL_TYPE_TEREDO,
+ TUNNEL_TYPE_IPHTTPS
+} TUNNEL_TYPE, *PTUNNEL_TYPE;
+
+/*
+A DUID consists of a two-octet type code represented in network byte
+ order, followed by a variable number of octets that make up the
+ actual identifier. A DUID can be no more than 128 octets long (not
+ including the type code).
+*/
+#define MAX_DHCPV6_DUID_LENGTH 130
+
+typedef union _NET_LUID {
+ ULONG64 Value;
+ struct {
+ ULONG64 Reserved :24;
+ ULONG64 NetLuidIndex :24;
+ ULONG64 IfType :16;
+ } Info;
+} NET_LUID, *PNET_LUID, IF_LUID;
+
+#define MAX_DNS_SUFFIX_STRING_LENGTH 246
+
+typedef struct _IP_ADAPTER_DNS_SUFFIX {
+ struct _IP_ADAPTER_DNS_SUFFIX *Next;
+ WCHAR String[MAX_DNS_SUFFIX_STRING_LENGTH];
+} IP_ADAPTER_DNS_SUFFIX, *PIP_ADAPTER_DNS_SUFFIX;
+
+
+
+#define _IP_ADAPTER_ADDRESSES_HEAD \
+ union { \
+ ULONGLONG Alignment; \
+ struct { \
+ ULONG Length; \
+ DWORD IfIndex; \
+ }; \
+ };
+
+#define _IP_ADAPTER_ADDRESSES_BASE \
+ PCHAR AdapterName; \
+ PIP_ADAPTER_UNICAST_ADDRESS FirstUnicastAddress; \
+ PIP_ADAPTER_ANYCAST_ADDRESS FirstAnycastAddress; \
+ PIP_ADAPTER_MULTICAST_ADDRESS FirstMulticastAddress; \
+ PIP_ADAPTER_DNS_SERVER_ADDRESS FirstDnsServerAddress; \
+ PWCHAR DnsSuffix; \
+ PWCHAR Description; \
+ PWCHAR FriendlyName; \
+ BYTE PhysicalAddress[MAX_ADAPTER_ADDRESS_LENGTH]; \
+ DWORD PhysicalAddressLength; \
+ DWORD Flags; \
+ DWORD Mtu; \
+ DWORD IfType; \
+ IF_OPER_STATUS OperStatus;
+
+#define _IP_ADAPTER_ADDRESSES_ADD_XPSP1 \
+ DWORD Ipv6IfIndex; \
+ DWORD ZoneIndices[16]; \
+ PIP_ADAPTER_PREFIX FirstPrefix; \
+
+
+#define _IP_ADAPTER_ADDRESSES_ADD_VISTA \
+ _IP_ADAPTER_ADDRESSES_ADD_XPSP1 \
+ ULONG64 TransmitLinkSpeed; \
+ ULONG64 ReceiveLinkSpeed; \
+ PIP_ADAPTER_WINS_SERVER_ADDRESS_LH FirstWinsServerAddress; \
+ PIP_ADAPTER_GATEWAY_ADDRESS_LH FirstGatewayAddress; \
+ ULONG Ipv4Metric; \
+ ULONG Ipv6Metric; \
+ IF_LUID Luid; \
+ SOCKET_ADDRESS Dhcpv4Server; \
+ NET_IF_COMPARTMENT_ID CompartmentId; \
+ NET_IF_NETWORK_GUID NetworkGuid; \
+ NET_IF_CONNECTION_TYPE ConnectionType; \
+ TUNNEL_TYPE TunnelType; \
+ SOCKET_ADDRESS Dhcpv6Server; \
+ BYTE Dhcpv6ClientDuid[MAX_DHCPV6_DUID_LENGTH]; \
+ ULONG Dhcpv6ClientDuidLength; \
+ ULONG Dhcpv6Iaid;
+
+#define _IP_ADAPTER_ADDRESSES_ADD_2008_OR_VISTASP1 \
+ _IP_ADAPTER_ADDRESSES_ADD_VISTA \
+ PIP_ADAPTER_DNS_SUFFIX FirstDnsSuffix;
+
+#define _IP_ADAPTER_ADDRESSES_DEFINE(suffix,addition) \
+typedef struct _IP_ADAPTER_ADDRESSES##suffix { \
+ _IP_ADAPTER_ADDRESSES_HEAD \
+ struct _IP_ADAPTER_ADDRESSES##suffix *Next; \
+ _IP_ADAPTER_ADDRESSES_BASE \
+ addition \
+} IP_ADAPTER_ADDRESSES##suffix, *PIP_ADAPTER_ADDRESSES##suffix;
+
+
+/* _IP_ADAPTER_ADDRESSES_DEFINE(,) defined in w32api headers */
+_IP_ADAPTER_ADDRESSES_DEFINE(_XPSP1,_IP_ADAPTER_ADDRESSES_ADD_XPSP1)
+_IP_ADAPTER_ADDRESSES_DEFINE(_VISTA,_IP_ADAPTER_ADDRESSES_ADD_VISTA)
+_IP_ADAPTER_ADDRESSES_DEFINE(_2008_OR_VISTASP1,_IP_ADAPTER_ADDRESSES_ADD_2008_OR_VISTASP1)
+
+static int
+EnumNICs_IPv6_get_ifs_count (SOCKET s)
+{
+ DWORD dwret = 0, err;
+ int iret;
+ iret = WSAIoctl (s, SIO_ADDRESS_LIST_QUERY, NULL, 0, NULL, 0,
+ &dwret, NULL, NULL);
+ err = GetLastError ();
+ if (iret == SOCKET_ERROR && err == WSAEFAULT)
+ return dwret;
+ else if (iret == 0)
+ return 0;
+ return GNUNET_SYSERR;
+}
+
+static int
+EnumNICs_IPv6_get_ifs (SOCKET s, SOCKET_ADDRESS_LIST *inf, int size)
+{
+ int iret;
+ DWORD dwret = 0;
+ iret = WSAIoctl (s, SIO_ADDRESS_LIST_QUERY, NULL, 0, inf, size,
+ &dwret, NULL, NULL);
+
+ if (iret != 0 || dwret != size)
+ {
+ /* It's supposed to succeed! And size should be the same */
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+#undef GNUNET_malloc
+#define GNUNET_malloc(a) HeapAlloc(GetProcessHeap (), HEAP_ZERO_MEMORY | \
+ HEAP_GENERATE_EXCEPTIONS, a)
+
+#undef GNUNET_free
+#define GNUNET_free(a) HeapFree(GetProcessHeap (), 0, a)
+
+#undef GNUNET_free_non_null
+#define GNUNET_free_non_null(a) do { if ((a) != NULL) GNUNET_free(a); } while (0)
+
+static int
+EnumNICs_IPv4_get_ifs (SOCKET s, INTERFACE_INFO **inf, int *size)
+{
+ int iret;
+ DWORD dwret = 0;
+ DWORD error;
+ INTERFACE_INFO *ii = NULL;
+ DWORD ii_size = sizeof (INTERFACE_INFO) * 15;
+ while (TRUE)
+ {
+ if (ii_size >= sizeof (INTERFACE_INFO) * 1000)
+ return GNUNET_SYSERR;
+ ii = (INTERFACE_INFO *) GNUNET_malloc (ii_size);
+ dwret = 0;
+ iret = WSAIoctl (s, SIO_GET_INTERFACE_LIST, NULL, 0, ii, ii_size,
+ &dwret, NULL, NULL);
+ error = GetLastError ();
+ if (iret == SOCKET_ERROR)
+ {
+ if (error == WSAEFAULT)
+ {
+ GNUNET_free (ii);
+ ii_size *= 2;
+ continue;
+ }
+ GNUNET_free (ii);
+ return GNUNET_SYSERR;
+ }
+ else
+ {
+ *inf = ii;
+ *size = dwret;
+ return GNUNET_OK;
+ }
+ }
+ return GNUNET_SYSERR;
+}
+
+int
+EnumNICs2 (INTERFACE_INFO **ifs4, int *ifs4_len, SOCKET_ADDRESS_LIST **ifs6)
+{
+ int result = 0;
+ SOCKET s4 = INVALID_SOCKET, s6 = INVALID_SOCKET;
+ DWORD dwret1 = 0, dwret2;
+ DWORD err1, err2;
+ int ifs4len = 0, ifs6len = 0;
+ INTERFACE_INFO *interfaces4 = NULL;
+ SOCKET_ADDRESS_LIST *interfaces6 = NULL;
+ SetLastError (0);
+ s4 = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ err1 = GetLastError ();
+ SetLastError (0);
+ s6 = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
+ err2 = GetLastError ();
+ if (s6 != INVALID_SOCKET)
+ {
+ ifs6len = EnumNICs_IPv6_get_ifs_count (s6);
+ if (ifs6len > 0)
+ {
+ interfaces6 = (SOCKET_ADDRESS_LIST *) GNUNET_malloc (ifs6len);
+ result = EnumNICs_IPv6_get_ifs (s6, interfaces6, ifs6len) || result;
+ }
+ closesocket (s6);
+ s6 = INVALID_SOCKET;
+ }
+
+ if (s4 != INVALID_SOCKET)
+ {
+ result = EnumNICs_IPv4_get_ifs (s4, &interfaces4, &ifs4len) || result;
+ closesocket (s4);
+ s4 = INVALID_SOCKET;
+ }
+ if (ifs6len + ifs4len == 0)
+ goto error;
+
+ if (!result)
+ {
+ *ifs4 = interfaces4;
+ *ifs4_len = ifs4len;
+ *ifs6 = interfaces6;
+ return GNUNET_OK;
+ }
+error:
+ if (interfaces4 != NULL)
+ GNUNET_free (interfaces4);
+ if (interfaces6 != NULL)
+ GNUNET_free (interfaces6);
+ if (s4 != INVALID_SOCKET)
+ closesocket (s4);
+ if (s6 != INVALID_SOCKET)
+ closesocket (s6);
+ return GNUNET_SYSERR;
+}
+
+/**
+ * Returns GNUNET_OK on OK, GNUNET_SYSERR on error
+ */
+int
+EnumNICs3 (struct EnumNICs3_results **results, int *results_count)
+{
+ DWORD dwRetVal = 0;
+ int count = 0;
+ ULONG flags = /*GAA_FLAG_INCLUDE_PREFIX |*/ GAA_FLAG_SKIP_ANYCAST |
+ GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
+ struct sockaddr_in6 examplecom6;
+ IPAddr examplecom;
+ DWORD best_interface = 0;
+ DWORD best_interface6 = 0;
+
+ int use_enum2 = 0;
+ INTERFACE_INFO *interfaces4 = NULL;
+ int interfaces4_len = 0;
+ SOCKET_ADDRESS_LIST *interfaces6 = NULL;
+
+ unsigned long outBufLen = sizeof (IP_ADAPTER_ADDRESSES);
+ IP_ADAPTER_ADDRESSES *pCurrentAddress = NULL;
+ IP_ADAPTER_ADDRESSES *pAddresses = (IP_ADAPTER_ADDRESSES *) GNUNET_malloc (outBufLen);
+
+ if (GetAdaptersAddresses (AF_UNSPEC, flags, NULL, pAddresses, &outBufLen)
+ == ERROR_BUFFER_OVERFLOW)
+ {
+ GNUNET_free (pAddresses);
+ pAddresses = (IP_ADAPTER_ADDRESSES *) GNUNET_malloc (outBufLen);
+ }
+
+ dwRetVal = GetAdaptersAddresses (AF_UNSPEC, flags, NULL, pAddresses, &outBufLen);
+
+ if (dwRetVal != NO_ERROR)
+ {
+ GNUNET_free (pAddresses);
+ return GNUNET_SYSERR;
+ }
+
+ if (pAddresses->Length < sizeof (IP_ADAPTER_ADDRESSES_VISTA))
+ {
+ use_enum2 = 1;
+
+ /* Enumerate NICs using WSAIoctl() */
+ if (GNUNET_OK != EnumNICs2 (&interfaces4, &interfaces4_len, &interfaces6))
+ {
+ GNUNET_free (pAddresses);
+ return GNUNET_SYSERR;
+ }
+ }
+
+ examplecom = inet_addr("192.0.34.166"); /* www.example.com */
+ if (GetBestInterface (examplecom, &best_interface) != NO_ERROR)
+ best_interface = 0;
+
+ if (GNGetBestInterfaceEx != NULL)
+ {
+ examplecom6.sin6_family = AF_INET6;
+ examplecom6.sin6_port = 0;
+ examplecom6.sin6_flowinfo = 0;
+ examplecom6.sin6_scope_id = 0;
+ inet_pton (AF_INET6, "2001:500:88:200:0:0:0:10",
+ (struct sockaddr *) &examplecom6.sin6_addr);
+ dwRetVal = GNGetBestInterfaceEx ((struct sockaddr *) &examplecom6,
+ &best_interface6);
+ if (dwRetVal != NO_ERROR)
+ best_interface6 = 0;
+ }
+
+ /* Give IPv6 a priority */
+ if (best_interface6 != 0)
+ best_interface = best_interface6;
+
+ count = 0;
+ for (pCurrentAddress = pAddresses;
+ pCurrentAddress != NULL; pCurrentAddress = pCurrentAddress->Next)
+ {
+ if (pCurrentAddress->OperStatus == IfOperStatusUp)
+ {
+ IP_ADAPTER_UNICAST_ADDRESS *unicast = NULL;
+ for (unicast = pCurrentAddress->FirstUnicastAddress; unicast != NULL;
+ unicast = unicast->Next)
+ {
+ if ((unicast->Address.lpSockaddr->sa_family == AF_INET ||
+ unicast->Address.lpSockaddr->sa_family == AF_INET6) &&
+ (unicast->DadState == IpDadStateDeprecated ||
+ unicast->DadState == IpDadStatePreferred))
+ count += 1;
+ }
+ }
+ }
+
+ if (count == 0)
+ {
+ *results = NULL;
+ *results_count = 0;
+ GNUNET_free (pAddresses);
+ GNUNET_free_non_null (interfaces4);
+ GNUNET_free_non_null (interfaces6);
+ return GNUNET_OK;
+ }
+
+ *results = (struct EnumNICs3_results *) GNUNET_malloc (
+ sizeof (struct EnumNICs3_results) * count);
+ *results_count = count;
+
+ count = 0;
+ for (pCurrentAddress = pAddresses;
+ pCurrentAddress != NULL; pCurrentAddress = pCurrentAddress->Next)
+ {
+ struct EnumNICs3_results *r;
+ IP_ADAPTER_UNICAST_ADDRESS *unicast = NULL;
+ if (pCurrentAddress->OperStatus != IfOperStatusUp)
+ continue;
+ for (unicast = pCurrentAddress->FirstUnicastAddress; unicast != NULL;
+ unicast = unicast->Next)
+ {
+ int i, j;
+ int mask_length = -1;
+ char dst[INET6_ADDRSTRLEN + 1];
+
+ if ((unicast->Address.lpSockaddr->sa_family != AF_INET &&
+ unicast->Address.lpSockaddr->sa_family != AF_INET6) ||
+ (unicast->DadState != IpDadStateDeprecated &&
+ unicast->DadState != IpDadStatePreferred))
+ continue;
+
+ r = &(*results)[count];
+ r->flags = 0;
+ if (pCurrentAddress->IfIndex > 0 &&
+ pCurrentAddress->IfIndex == best_interface &&
+ unicast->Address.lpSockaddr->sa_family == AF_INET)
+ r->is_default = 1;
+ else if (pCurrentAddress->Ipv6IfIndex > 0 &&
+ pCurrentAddress->Ipv6IfIndex == best_interface6 &&
+ unicast->Address.lpSockaddr->sa_family == AF_INET6)
+ r->is_default = 1;
+ else
+ r->is_default = 0;
+
+ /* Don't choose default interface twice */
+ if (r->is_default)
+ best_interface = best_interface6 = 0;
+
+ if (!use_enum2)
+ {
+ memcpy (&r->address, unicast->Address.lpSockaddr,
+ unicast->Address.iSockaddrLength);
+ memset (&r->mask, 0, sizeof (struct sockaddr));
+ mask_length = ((IP_ADAPTER_UNICAST_ADDRESS_VISTA *) unicast)->
+ OnLinkPrefixLength;
+ /* OnLinkPrefixLength is the number of leading 1s in the mask.
+ * OnLinkPrefixLength is available on Vista and later (hence use_enum2).
+ */
+ if (unicast->Address.lpSockaddr->sa_family == AF_INET)
+ {
+ struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
+ for (i = 0; i < mask_length; i++)
+ ((unsigned char *) &m->sin_addr)[i / 8] |= 0x80 >> (i % 8);
+ }
+ else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *m = (struct sockaddr_in6 *) &r->mask;
+ struct sockaddr_in6 *b = (struct sockaddr_in6 *) &r->broadcast;
+ for (i = 0; i < mask_length; i++)
+ ((unsigned char *) &m->sin6_addr)[i / 8] |= 0x80 >> (i % 8);
+ memcpy (&r->broadcast, &r->address, unicast->Address.iSockaddrLength);
+ for (i = mask_length; i < 128; i++)
+ ((unsigned char *) &b->sin6_addr)[i / 8] |= 0x80 >> (i % 8);
+ }
+ r->flags |= ENUMNICS3_MASK_OK;
+ }
+ else
+ {
+ int found = 0;
+ if (unicast->Address.lpSockaddr->sa_family == AF_INET)
+ {
+ for (i = 0; !found && i < interfaces4_len / sizeof (INTERFACE_INFO); i++)
+ {
+ struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
+ if (memcpy (&interfaces4[i].iiAddress.Address,
+ unicast->Address.lpSockaddr,
+ unicast->Address.iSockaddrLength) != 0)
+ continue;
+ found = 1;
+ memcpy (&r->address, &interfaces4[i].iiAddress.Address,
+ sizeof (struct sockaddr_in));
+ memcpy (&r->mask, &interfaces4[i].iiNetmask.Address,
+ sizeof (struct sockaddr_in));
+ for (mask_length = 0;
+ ((unsigned char *) &m->sin_addr)[mask_length / 8] &
+ 0x80 >> (mask_length % 8); mask_length++)
+ {
+ }
+ r->flags |= ENUMNICS3_MASK_OK;
+ }
+ }
+ else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
+ {
+ for (i = 0;
+ interfaces6 != NULL && !found && i < interfaces6->iAddressCount;
+ i++)
+ {
+ if (memcpy (interfaces6->Address[i].lpSockaddr,
+ unicast->Address.lpSockaddr,
+ unicast->Address.iSockaddrLength) != 0)
+ continue;
+ found = 1;
+ memcpy (&r->address, interfaces6->Address[i].lpSockaddr,
+ sizeof (struct sockaddr_in6));
+ /* TODO: Find a way to reliably get network mask for IPv6 on XP */
+ memset (&r->mask, 0, sizeof (struct sockaddr));
+ r->flags &= ~ENUMNICS3_MASK_OK;
+ }
+ }
+ if (!found)
+ {
+ DebugBreak ();
+ }
+ }
+ if (unicast->Address.lpSockaddr->sa_family == AF_INET)
+ {
+ struct sockaddr_in *m = (struct sockaddr_in *) &r->mask;
+ struct sockaddr_in *a = (struct sockaddr_in *) &r->address;
+ /* copy address to broadcast, then flip all the trailing bits not
+ * falling under netmask to 1,
+ * so we get, 192.168.0.255 from, say, 192.168.0.43 with mask == 24.
+ */
+ memcpy (&r->broadcast, &r->address, unicast->Address.iSockaddrLength);
+ for (i = mask_length; i < 32; i++)
+ ((unsigned char *) &m->sin_addr)[i / 8] |= 0x80 >> (i % 8);
+ r->flags |= ENUMNICS3_BCAST_OK;
+ r->addr_size = sizeof (struct sockaddr_in);
+ inet_ntop (AF_INET, &a->sin_addr, dst, INET_ADDRSTRLEN);
+ }
+ else if (unicast->Address.lpSockaddr->sa_family == AF_INET6)
+ {
+ struct sockaddr_in6 *a = (struct sockaddr_in6 *) &r->address;
+ /* for IPv6 broadcast is not defined, zero it down */
+ memset (&r->broadcast, 0, sizeof (struct sockaddr));
+ r->flags &= ~ENUMNICS3_BCAST_OK;
+ r->addr_size = sizeof (struct sockaddr_in6);
+ inet_ntop (AF_INET6, &a->sin6_addr, dst, INET6_ADDRSTRLEN);
+ }
+
+ i = 0;
+ i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0,
+ "%S (%s", pCurrentAddress->FriendlyName, dst);
+ for (j = 0; j < pCurrentAddress->PhysicalAddressLength; j++)
+ i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0,
+ "%s%02X",j > 0 ? ":" : " - ", pCurrentAddress->PhysicalAddress[j]);
+ i += snprintf (&r->pretty_name[i], 1000 - i > 0 ? 1000 - i : 0, ")");
+ r->pretty_name[1000] = '\0';
+ count += 1;
+ }
+ }
+
+ if (use_enum2)
+ {
+ GNUNET_free_non_null (interfaces4);
+ GNUNET_free_non_null (interfaces6);
+ }
+
+ GNUNET_free (pAddresses);
+ return GNUNET_OK;
+}
+
+void
+EnumNICs3_free (struct EnumNICs3_results *r)
+{
+ GNUNET_free_non_null (r);
+}
+
+
+/**
+ * Lists all network interfaces in a combo box
+ * Used by the basic GTK configurator
+ *
+ * @param callback function to call for each NIC
+ * @param callback_cls closure for callback
+ */
+int
+ListNICs (void (*callback) (void *, const char *, int), void * callback_cls)
+{
+ int r;
+ int i;
+ struct EnumNICs3_results *results = NULL;
+ int results_count;
+
+ r = EnumNICs3 (&results, &results_count);
+ if (r != GNUNET_OK)
+ return GNUNET_NO;
+
+ for (i = 0; i < results_count; i++)
+ callback (callback_cls, results[i].pretty_name, results[i].is_default);
+ GNUNET_free_non_null (results);
+ return GNUNET_YES;
+}
+
+/**
+ * @brief Installs the Windows service
+ * @param servicename name of the service as diplayed by the SCM
+ * @param application path to the application binary
+ * @param username the name of the service's user account
+ * @returns 0 on success
+ * 1 if the Windows version doesn't support services
+ * 2 if the SCM could not be opened
+ * 3 if the service could not be created
+ */
+int InstallAsService(char *servicename, char *application, char *username)
+{
+ SC_HANDLE hManager, hService;
+ char szEXE[_MAX_PATH + 17] = "\"";
+ char *user = NULL;
+
+ if (! GNOpenSCManager)
+ return 1;
+
+ plibc_conv_to_win_path(application, szEXE + 1);
+ strcat(szEXE, "\" --win-service");
+ hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
+ if (! hManager)
+ return 2;
+
+ if (username)
+ {
+ user = (char *) malloc(strlen(username) + 3);
+ sprintf(user, ".\\%s", username);
+ }
+
+ hService = GNCreateService(hManager, (LPCTSTR) servicename, (LPCTSTR) servicename, 0,
+ SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, (LPCTSTR) szEXE,
+ NULL, NULL, NULL, (LPCTSTR) user, (LPCTSTR) username);
+
+ if (user)
+ free(user);
+
+ if (! hService)
+ return 3;
+
+ GNCloseServiceHandle(hService);
+
+ return 0;
+}
+
+/**
+ * @brief Uninstall Windows service
+ * @param servicename name of the service to delete
+ * @returns 0 on success
+ * 1 if the Windows version doesn't support services
+ * 2 if the SCM could not be openend
+ * 3 if the service cannot be accessed
+ * 4 if the service cannot be deleted
+ */
+int UninstallService(char *servicename)
+{
+ SC_HANDLE hManager, hService;
+
+ if (! GNOpenSCManager)
+ return 1;
+
+ hManager = GNOpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
+ if (! hManager)
+ return 2;
+
+ if (! (hService = GNOpenService(hManager, (LPCTSTR) servicename, DELETE)))
+ if (GetLastError() != ERROR_SERVICE_DOES_NOT_EXIST)
+ return 3;
+ else
+ goto closeSCM;
+
+ if (! GNDeleteService(hService))
+ if (GetLastError() != ERROR_SERVICE_MARKED_FOR_DELETE)
+ return 4;
+
+closeSCM:
+ GNCloseServiceHandle(hService);
+
+ return 0;
+}
+
+/**
+ * @author Scott Field, Microsoft
+ * @see http://support.microsoft.com/?scid=kb;en-us;132958
+ * @date 12-Jul-95
+ */
+void _InitLsaString(PLSA_UNICODE_STRING LsaString, LPWSTR String)
+{
+ DWORD StringLength;
+
+ if(String == NULL)
+ {
+ LsaString->Buffer = NULL;
+ LsaString->Length = 0;
+ LsaString->MaximumLength = 0;
+ return;
+ }
+
+ StringLength = wcslen(String);
+ LsaString->Buffer = String;
+ LsaString->Length = (USHORT) StringLength *sizeof(WCHAR);
+ LsaString->MaximumLength = (USHORT) (StringLength + 1) * sizeof(WCHAR);
+}
+
+
+/**
+ * @author Scott Field, Microsoft
+ * @see http://support.microsoft.com/?scid=kb;en-us;132958
+ * @date 12-Jul-95
+ */
+NTSTATUS _OpenPolicy(LPWSTR ServerName, DWORD DesiredAccess, PLSA_HANDLE PolicyHandle)
+{
+ LSA_OBJECT_ATTRIBUTES ObjectAttributes;
+ LSA_UNICODE_STRING ServerString;
+ PLSA_UNICODE_STRING Server = NULL;
+
+ /* Always initialize the object attributes to all zeroes. */
+ ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));
+
+ if(ServerName != NULL)
+ {
+ /* Make a LSA_UNICODE_STRING out of the LPWSTR passed in */
+ _InitLsaString(&ServerString, ServerName);
+ Server = &ServerString;
+ }
+
+ /* Attempt to open the policy. */
+ return GNLsaOpenPolicy(Server,
+ &ObjectAttributes, DesiredAccess, PolicyHandle);
+}
+
+/**
+ * @brief Obtain a SID representing the supplied account on the supplied system
+ * @return TRUE on success, FALSE on failure
+ * @author Scott Field, Microsoft
+ * @date 12-Jul-95
+ * @remarks A buffer is allocated which contains the SID representing the
+ * supplied account. This buffer should be freed when it is no longer
+ * needed by calling\n
+ * HeapFree(GetProcessHeap(), 0, buffer)
+ * @remarks Call GetLastError() to obtain extended error information.
+ * @see http://support.microsoft.com/?scid=kb;en-us;132958
+ */
+BOOL _GetAccountSid(LPCTSTR SystemName, LPCTSTR AccountName, PSID * Sid)
+{
+ LPTSTR ReferencedDomain = NULL;
+ DWORD cbSid = 128; /* initial allocation attempt */
+ DWORD cchReferencedDomain = 16; /* initial allocation size */
+ SID_NAME_USE peUse;
+ BOOL bSuccess = FALSE; /* assume this function will fail */
+
+ /* initial memory allocations */
+ if ((*Sid = HeapAlloc (GetProcessHeap (), 0, cbSid)) == NULL)
+ return FALSE;
+
+ if ((ReferencedDomain = (LPTSTR) HeapAlloc (GetProcessHeap (),
+ 0,
+ cchReferencedDomain *
+ sizeof (TCHAR))) == NULL)
+ return FALSE;
+
+ /* Obtain the SID of the specified account on the specified system. */
+ while (!GNLookupAccountName(SystemName, /* machine to lookup account on */
+ AccountName, /* account to lookup */
+ *Sid, /* SID of interest */
+ &cbSid, /* size of SID */
+ ReferencedDomain, /* domain account was found on */
+ &cchReferencedDomain, &peUse))
+ {
+ if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
+ {
+ /* reallocate memory */
+ if ((*Sid = HeapReAlloc (GetProcessHeap (), 0, *Sid, cbSid)) == NULL)
+ return FALSE;
+
+ if ((ReferencedDomain = (LPTSTR) HeapReAlloc (GetProcessHeap (),
+ 0,
+ ReferencedDomain,
+ cchReferencedDomain
+ * sizeof (TCHAR))) == NULL)
+ return FALSE;
+ }
+ else
+ goto end;
+ }
+
+ /* Indicate success. */
+ bSuccess = TRUE;
+
+end:
+ /* Cleanup and indicate failure, if appropriate. */
+ HeapFree (GetProcessHeap (), 0, ReferencedDomain);
+
+ if (!bSuccess)
+ {
+ if (*Sid != NULL)
+ {
+ HeapFree (GetProcessHeap (), 0, *Sid);
+ *Sid = NULL;
+ }
+ }
+
+ return bSuccess;
+}
+
+/**
+ * @author Scott Field, Microsoft
+ * @see http://support.microsoft.com/?scid=kb;en-us;132958
+ * @date 12-Jul-95
+ */
+NTSTATUS _SetPrivilegeOnAccount(LSA_HANDLE PolicyHandle,/* open policy handle */
+ PSID AccountSid, /* SID to grant privilege to */
+ LPWSTR PrivilegeName, /* privilege to grant (Unicode) */
+ BOOL bEnable /* enable or disable */
+ )
+{
+ LSA_UNICODE_STRING PrivilegeString;
+
+ /* Create a LSA_UNICODE_STRING for the privilege name. */
+ _InitLsaString(&PrivilegeString, PrivilegeName);
+
+ /* grant or revoke the privilege, accordingly */
+ if(bEnable)
+ {
+ NTSTATUS i;
+
+ i = GNLsaAddAccountRights(PolicyHandle, /* open policy handle */
+ AccountSid, /* target SID */
+ &PrivilegeString, /* privileges */
+ 1 /* privilege count */
+ );
+ }
+ else
+ {
+ return GNLsaRemoveAccountRights(PolicyHandle, /* open policy handle */
+ AccountSid, /* target SID */
+ FALSE, /* do not disable all rights */
+ &PrivilegeString, /* privileges */
+ 1 /* privilege count */
+ );
+ }
+}
+
+/**
+ * @brief Create a Windows service account
+ * @return 0 on success, > 0 otherwise
+ * @param pszName the name of the account
+ * @param pszDesc description of the account
+ */
+int CreateServiceAccount(const char *pszName, const char *pszDesc)
+{
+ USER_INFO_1 ui;
+ USER_INFO_1008 ui2;
+ NET_API_STATUS nStatus;
+ wchar_t wszName[MAX_NAME_LENGTH], wszDesc[MAX_NAME_LENGTH];
+ DWORD dwErr;
+ LSA_HANDLE hPolicy;
+ PSID pSID;
+
+ if (! GNNetUserAdd)
+ return 1;
+ mbstowcs(wszName, pszName, strlen(pszName) + 1);
+ mbstowcs(wszDesc, pszDesc, strlen(pszDesc) + 1);
+
+ memset(&ui, 0, sizeof(ui));
+ ui.usri1_name = wszName;
+ ui.usri1_password = wszName; /* account is locked anyway */
+ ui.usri1_priv = USER_PRIV_USER;
+ ui.usri1_comment = wszDesc;
+ ui.usri1_flags = UF_SCRIPT;
+
+ nStatus = GNNetUserAdd(NULL, 1, (LPBYTE)&ui, NULL);
+
+ if (nStatus != NERR_Success && nStatus != NERR_UserExists)
+ return 2;
+
+ ui2.usri1008_flags = UF_PASSWD_CANT_CHANGE | UF_DONT_EXPIRE_PASSWD;
+ GNNetUserSetInfo(NULL, wszName, 1008, (LPBYTE)&ui2, NULL);
+
+ if (_OpenPolicy(NULL, POLICY_ALL_ACCESS, &hPolicy) !=
+ STATUS_SUCCESS)
+ return 3;
+
+ _GetAccountSid(NULL, (LPCTSTR) pszName, &pSID);
+
+ if (_SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeServiceLogonRight", TRUE) != STATUS_SUCCESS)
+ return 4;
+
+ _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyInteractiveLogonRight", TRUE);
+ _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyBatchLogonRight", TRUE);
+ _SetPrivilegeOnAccount(hPolicy, pSID, (LPWSTR) L"SeDenyNetworkLogonRight", TRUE);
+
+ GNLsaClose(hPolicy);
+
+ return 0;
+}
+
+/**
+ * @brief Grant permission to a file
+ * @param lpszFileName the name of the file or directory
+ * @param lpszAccountName the user account
+ * @param dwAccessMask the desired access (e.g. GENERIC_ALL)
+ * @return TRUE on success
+ * @remark based on http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q102102&
+ */
+BOOL AddPathAccessRights(char *lpszFileName, char *lpszAccountName,
+ DWORD dwAccessMask)
+{
+ /* SID variables. */
+ SID_NAME_USE snuType;
+ TCHAR * szDomain = NULL;
+ DWORD cbDomain = 0;
+ LPVOID pUserSID = NULL;
+ DWORD cbUserSID = 0;
+
+ /* File SD variables. */
+ PSECURITY_DESCRIPTOR pFileSD = NULL;
+ DWORD cbFileSD = 0;
+
+ /* New SD variables. */
+ SECURITY_DESCRIPTOR newSD;
+
+ /* ACL variables. */
+ PACL pACL = NULL;
+ BOOL fDaclPresent;
+ BOOL fDaclDefaulted;
+ ACL_SIZE_INFORMATION AclInfo;
+
+ /* New ACL variables. */
+ PACL pNewACL = NULL;
+ DWORD cbNewACL = 0;
+
+ /* Temporary ACE. */
+ LPVOID pTempAce = NULL;
+ UINT CurrentAceIndex = 0;
+
+ UINT newAceIndex = 0;
+
+ /* Assume function will fail. */
+ BOOL fResult = FALSE;
+ BOOL fAPISuccess;
+
+ SECURITY_INFORMATION secInfo = DACL_SECURITY_INFORMATION;
+
+ /**
+ * STEP 1: Get SID of the account name specified.
+ */
+ fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,
+ pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);
+
+ /* API should have failed with insufficient buffer. */
+ if (fAPISuccess)
+ goto end;
+ else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ goto end;
+ }
+
+ pUserSID = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbUserSID);
+ if (!pUserSID) {
+ goto end;
+ }
+
+ szDomain = (TCHAR *) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbDomain * sizeof(TCHAR));
+ if (!szDomain) {
+ goto end;
+ }
+
+ fAPISuccess = GNLookupAccountName(NULL, (LPCTSTR) lpszAccountName,
+ pUserSID, &cbUserSID, (LPTSTR) szDomain, &cbDomain, &snuType);
+ if (!fAPISuccess) {
+ goto end;
+ }
+
+ /**
+ * STEP 2: Get security descriptor (SD) of the file specified.
+ */
+ fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,
+ secInfo, pFileSD, 0, &cbFileSD);
+
+ /* API should have failed with insufficient buffer. */
+ if (fAPISuccess)
+ goto end;
+ else if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
+ goto end;
+ }
+
+ pFileSD = (PSECURITY_DESCRIPTOR) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ cbFileSD);
+ if (!pFileSD) {
+ goto end;
+ }
+
+ fAPISuccess = GNGetFileSecurity((LPCTSTR) lpszFileName,
+ secInfo, pFileSD, cbFileSD, &cbFileSD);
+ if (!fAPISuccess) {
+ goto end;
+ }
+
+ /**
+ * STEP 3: Initialize new SD.
+ */
+ if (!GNInitializeSecurityDescriptor(&newSD,
+ SECURITY_DESCRIPTOR_REVISION)) {
+ goto end;
+ }
+
+ /**
+ * STEP 4: Get DACL from the old SD.
+ */
+ if (!GNGetSecurityDescriptorDacl(pFileSD, &fDaclPresent, &pACL,
+ &fDaclDefaulted)) {
+ goto end;
+ }
+
+ /**
+ * STEP 5: Get size information for DACL.
+ */
+ AclInfo.AceCount = 0; // Assume NULL DACL.
+ AclInfo.AclBytesFree = 0;
+ AclInfo.AclBytesInUse = sizeof(ACL);
+
+ if (pACL == NULL)
+ fDaclPresent = FALSE;
+
+ /* If not NULL DACL, gather size information from DACL. */
+ if (fDaclPresent) {
+
+ if (!GNGetAclInformation(pACL, &AclInfo,
+ sizeof(ACL_SIZE_INFORMATION), AclSizeInformation)) {
+ goto end;
+ }
+ }
+
+ /**
+ * STEP 6: Compute size needed for the new ACL.
+ */
+ cbNewACL = AclInfo.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE)
+ + GetLengthSid(pUserSID) - sizeof(DWORD);
+
+ /**
+ * STEP 7: Allocate memory for new ACL.
+ */
+ pNewACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbNewACL);
+ if (!pNewACL) {
+ goto end;
+ }
+
+ /**
+ * STEP 8: Initialize the new ACL.
+ */
+ if (!GNInitializeAcl(pNewACL, cbNewACL, ACL_REVISION2)) {
+ goto end;
+ }
+
+ /**
+ * STEP 9 If DACL is present, copy all the ACEs from the old DACL
+ * to the new DACL.
+ *
+ * The following code assumes that the old DACL is
+ * already in Windows 2000 preferred order. To conform
+ * to the new Windows 2000 preferred order, first we will
+ * copy all non-inherited ACEs from the old DACL to the
+ * new DACL, irrespective of the ACE type.
+ */
+
+ newAceIndex = 0;
+
+ if (fDaclPresent && AclInfo.AceCount) {
+
+ for (CurrentAceIndex = 0;
+ CurrentAceIndex < AclInfo.AceCount;
+ CurrentAceIndex++) {
+
+ /**
+ * TEP 10: Get an ACE.
+ */
+ if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {
+ goto end;
+ }
+
+ /**
+ * STEP 11: Check if it is a non-inherited ACE.
+ * If it is an inherited ACE, break from the loop so
+ * that the new access allowed non-inherited ACE can
+ * be added in the correct position, immediately after
+ * all non-inherited ACEs.
+ */
+ if (((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags
+ & INHERITED_ACE)
+ break;
+
+ /**
+ * STEP 12: Skip adding the ACE, if the SID matches
+ * with the account specified, as we are going to
+ * add an access allowed ACE with a different access
+ * mask.
+ */
+ if (GNEqualSid(pUserSID,
+ &(((ACCESS_ALLOWED_ACE *)pTempAce)->SidStart)))
+ continue;
+
+ /**
+ * STEP 13: Add the ACE to the new ACL.
+ */
+ if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
+ ((PACE_HEADER) pTempAce)->AceSize)) {
+ goto end;
+ }
+
+ newAceIndex++;
+ }
+ }
+
+ /**
+ * STEP 14: Add the access-allowed ACE to the new DACL.
+ * The new ACE added here will be in the correct position,
+ * immediately after all existing non-inherited ACEs.
+ */
+ if (!GNAddAccessAllowedAce(pNewACL, ACL_REVISION2, dwAccessMask,
+ pUserSID)) {
+ goto end;
+ }
+
+ /**
+ * STEP 14.5: Make new ACE inheritable
+ */
+ if (!GetAce(pNewACL, newAceIndex, &pTempAce))
+ goto end;
+ ((ACCESS_ALLOWED_ACE *)pTempAce)->Header.AceFlags |=
+ (CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE);
+
+ /**
+ * STEP 15: To conform to the new Windows 2000 preferred order,
+ * we will now copy the rest of inherited ACEs from the
+ * old DACL to the new DACL.
+ */
+ if (fDaclPresent && AclInfo.AceCount) {
+
+ for (;
+ CurrentAceIndex < AclInfo.AceCount;
+ CurrentAceIndex++) {
+
+ /**
+ * STEP 16: Get an ACE.
+ */
+ if (!GNGetAce(pACL, CurrentAceIndex, &pTempAce)) {
+ goto end;
+ }
+
+ /**
+ * STEP 17: Add the ACE to the new ACL.
+ */
+ if (!GNAddAce(pNewACL, ACL_REVISION, MAXDWORD, pTempAce,
+ ((PACE_HEADER) pTempAce)->AceSize)) {
+ goto end;
+ }
+ }
+ }
+
+ /**
+ * STEP 18: Set permissions
+ */
+ if (GNSetNamedSecurityInfo((LPTSTR) lpszFileName, SE_FILE_OBJECT,
+ DACL_SECURITY_INFORMATION, NULL, NULL, pNewACL, NULL) != ERROR_SUCCESS) {
+ goto end;
+ }
+
+ fResult = TRUE;
+
+end:
+
+ /**
+ * STEP 19: Free allocated memory
+ */
+ if (pUserSID)
+ HeapFree(GetProcessHeap(), 0, pUserSID);
+
+ if (szDomain)
+ HeapFree(GetProcessHeap(), 0, szDomain);
+
+ if (pFileSD)
+ HeapFree(GetProcessHeap(), 0, pFileSD);
+
+ if (pNewACL)
+ HeapFree(GetProcessHeap(), 0, pNewACL);
+
+ return fResult;
+}
+
+char *winErrorStr(const char *prefix, int dwErr)
+{
+ char *err, *ret;
+ int mem;
+
+ if (! FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL, (DWORD) dwErr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &err,
+ 0, NULL ))
+ {
+ err = (char *) LocalAlloc (LMEM_FIXED | LMEM_ZEROINIT, 1);
+ }
+
+ mem = strlen(err) + strlen(prefix) + 20;
+ ret = (char *) malloc(mem);
+
+ snprintf(ret, mem, "%s: %s (#%u)", prefix, err, dwErr);
+
+ LocalFree(err);
+
+ return ret;
+}
+
+} /* extern "C" */
+
+#endif
diff --git a/src/util/winproc.c b/src/util/winproc.c
new file mode 100644
index 0000000..7cd80a9
--- /dev/null
+++ b/src/util/winproc.c
@@ -0,0 +1,338 @@
+/*
+ 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/winproc.c
+ * @brief Functions for MS Windows
+ * @author Nils Durner
+ */
+
+#include "platform.h"
+#include "gnunet_common.h"
+
+#define DEBUG_WINPROC 0
+
+#ifdef MINGW
+
+static HINSTANCE hNTDLL, hIphlpapi, hAdvapi, hNetapi;
+#ifdef W32_VEH
+static void *GNWinVEH_handle = NULL;
+#endif
+
+TNtQuerySystemInformation GNNtQuerySystemInformation;
+TGetIfEntry GNGetIfEntry;
+TGetIpAddrTable GNGetIpAddrTable;
+TGetIfTable GNGetIfTable;
+TOpenSCManager GNOpenSCManager;
+TCreateService GNCreateService;
+TCloseServiceHandle GNCloseServiceHandle;
+TDeleteService GNDeleteService;
+TRegisterServiceCtrlHandler GNRegisterServiceCtrlHandler;
+TSetServiceStatus GNSetServiceStatus;
+TStartServiceCtrlDispatcher GNStartServiceCtrlDispatcher;
+TControlService GNControlService;
+TOpenService GNOpenService;
+TGetBestInterfaceEx GNGetBestInterfaceEx;
+TGetAdaptersInfo GNGetAdaptersInfo;
+TNetUserAdd GNNetUserAdd;
+TNetUserSetInfo GNNetUserSetInfo;
+TLsaOpenPolicy GNLsaOpenPolicy;
+TLsaAddAccountRights GNLsaAddAccountRights;
+TLsaRemoveAccountRights GNLsaRemoveAccountRights;
+TLsaClose GNLsaClose;
+TLookupAccountName GNLookupAccountName;
+TGetFileSecurity GNGetFileSecurity;
+TInitializeSecurityDescriptor GNInitializeSecurityDescriptor;
+TGetSecurityDescriptorDacl GNGetSecurityDescriptorDacl;
+TGetAclInformation GNGetAclInformation;
+TInitializeAcl GNInitializeAcl;
+TGetAce GNGetAce;
+TEqualSid GNEqualSid;
+TAddAce GNAddAce;
+TAddAccessAllowedAce GNAddAccessAllowedAce;
+TSetNamedSecurityInfo GNSetNamedSecurityInfo;
+
+#define LOG(kind,...) GNUNET_log_from (kind, "winproc", __VA_ARGS__)
+/**
+ * Log (panic) messages from PlibC
+ */
+void
+plibc_panic (int err, char *msg)
+{
+ LOG (((err == INT_MAX) ? GNUNET_ERROR_TYPE_DEBUG : GNUNET_ERROR_TYPE_ERROR),
+ "%s", msg);
+}
+
+#ifdef W32_VEH
+/**
+ * Handles exceptions (useful for debugging).
+ * Issues a DebugBreak() call if the process is being debugged (not really
+ * useful - if the process is being debugged, this handler won't be invoked
+ * anyway). If it is not, runs a debugger from GNUNET_DEBUGGER env var,
+ * substituting first %u in it for PID, and the second one for the event,
+ * that should be set once the debugger attaches itself (otherwise the
+ * only way out of WaitForSingleObject() is to time out after 1 minute).
+ */
+LONG __stdcall
+GNWinVEH (PEXCEPTION_POINTERS ExceptionInfo)
+{
+ char debugger[MAX_PATH + 1];
+ char *debugger_env = NULL;
+ if (IsDebuggerPresent ())
+ {
+ DebugBreak ();
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ debugger_env = getenv ("GNUNET_DEBUGGER");
+ if (debugger_env != NULL)
+ {
+ STARTUPINFO si;
+ PROCESS_INFORMATION pi;
+ HANDLE event;
+ SECURITY_ATTRIBUTES sa;
+ memset (&si, 0, sizeof (si));
+ si.cb = sizeof (si);
+ memset (&pi, 0, sizeof (pi));
+ memset (&sa, 0, sizeof (sa));
+ sa.nLength = sizeof (sa);
+ sa.bInheritHandle = TRUE;
+ event = CreateEvent (&sa, FALSE, FALSE, NULL);
+ snprintf (debugger, MAX_PATH + 1, debugger_env, GetCurrentProcessId (), (uintptr_t) event);
+ debugger[MAX_PATH] = '\0';
+ if (0 != CreateProcessA (NULL, debugger, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi))
+ {
+ CloseHandle (pi.hProcess);
+ CloseHandle (pi.hThread);
+ WaitForSingleObject (event, 60000);
+ CloseHandle (event);
+ if (IsDebuggerPresent ())
+ {
+ return EXCEPTION_CONTINUE_EXECUTION;
+ }
+ }
+ else
+ CloseHandle (event);
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif
+
+/**
+ * @brief Initialize PlibC and set up Windows environment
+ * @param logging context, NULL means stderr
+ * @return Error code from winerror.h, ERROR_SUCCESS on success
+*/
+int
+GNInitWinEnv ()
+{
+ int ret;
+
+ plibc_initialized ();
+ plibc_set_panic_proc (plibc_panic);
+ ret = plibc_init ("GNU", PACKAGE);
+
+ /* don't load other DLLs twice */
+ if (hNTDLL)
+ return ret;
+
+#ifdef W32_VEH
+ if (GNWinVEH_handle == NULL)
+ {
+ GNWinVEH_handle = AddVectoredExceptionHandler (1, &GNWinVEH);
+ if (GNWinVEH_handle == NULL)
+ {
+ /* This is bad, but what can we do? */
+ printf ("Failed to set up an exception handler!\n");
+ }
+ }
+#endif
+
+ hNTDLL = LoadLibrary ("ntdll.dll");
+
+ /* Function to get CPU usage under Win NT */
+ if (hNTDLL)
+ {
+ GNNtQuerySystemInformation =
+ (TNtQuerySystemInformation) GetProcAddress (hNTDLL,
+ "NtQuerySystemInformation");
+ }
+ else
+ {
+ GNNtQuerySystemInformation = NULL;
+ }
+
+ /* Functions to get information about a network adapter */
+ hIphlpapi = LoadLibrary ("iphlpapi.dll");
+ if (hIphlpapi)
+ {
+ GNGetIfEntry = (TGetIfEntry) GetProcAddress (hIphlpapi, "GetIfEntry");
+ GNGetIpAddrTable =
+ (TGetIpAddrTable) GetProcAddress (hIphlpapi, "GetIpAddrTable");
+ GNGetIfTable = (TGetIfTable) GetProcAddress (hIphlpapi, "GetIfTable");
+ GNGetBestInterfaceEx =
+ (TGetBestInterfaceEx) GetProcAddress (hIphlpapi, "GetBestInterfaceEx");
+ GNGetAdaptersInfo =
+ (TGetAdaptersInfo) GetProcAddress (hIphlpapi, "GetAdaptersInfo");
+ }
+ else
+ {
+ GNGetIfEntry = NULL;
+ GNGetIpAddrTable = NULL;
+ GNGetIfTable = NULL;
+ GNGetBestInterfaceEx = NULL;
+ GNGetAdaptersInfo = NULL;
+ }
+
+ /* Service & Account functions */
+ hAdvapi = LoadLibrary ("advapi32.dll");
+ if (hAdvapi)
+ {
+ GNOpenSCManager =
+ (TOpenSCManager) GetProcAddress (hAdvapi, "OpenSCManagerA");
+ GNCreateService =
+ (TCreateService) GetProcAddress (hAdvapi, "CreateServiceA");
+ GNCloseServiceHandle =
+ (TCloseServiceHandle) GetProcAddress (hAdvapi, "CloseServiceHandle");
+ GNDeleteService =
+ (TDeleteService) GetProcAddress (hAdvapi, "DeleteService");
+ GNRegisterServiceCtrlHandler =
+ (TRegisterServiceCtrlHandler) GetProcAddress (hAdvapi,
+ "RegisterServiceCtrlHandlerA");
+ GNSetServiceStatus =
+ (TSetServiceStatus) GetProcAddress (hAdvapi, "SetServiceStatus");
+ GNStartServiceCtrlDispatcher =
+ (TStartServiceCtrlDispatcher) GetProcAddress (hAdvapi,
+ "StartServiceCtrlDispatcherA");
+ GNControlService =
+ (TControlService) GetProcAddress (hAdvapi, "ControlService");
+ GNOpenService = (TOpenService) GetProcAddress (hAdvapi, "OpenServiceA");
+
+ GNLsaOpenPolicy =
+ (TLsaOpenPolicy) GetProcAddress (hAdvapi, "LsaOpenPolicy");
+ GNLsaAddAccountRights =
+ (TLsaAddAccountRights) GetProcAddress (hAdvapi, "LsaAddAccountRights");
+ GNLsaRemoveAccountRights =
+ (TLsaRemoveAccountRights) GetProcAddress (hAdvapi,
+ "LsaRemoveAccountRights");
+ GNLsaClose = (TLsaClose) GetProcAddress (hAdvapi, "LsaClose");
+ GNLookupAccountName =
+ (TLookupAccountName) GetProcAddress (hAdvapi, "LookupAccountNameA");
+
+ GNGetFileSecurity =
+ (TGetFileSecurity) GetProcAddress (hAdvapi, "GetFileSecurityA");
+ GNInitializeSecurityDescriptor =
+ (TInitializeSecurityDescriptor) GetProcAddress (hAdvapi,
+ "InitializeSecurityDescriptor");
+ GNGetSecurityDescriptorDacl =
+ (TGetSecurityDescriptorDacl) GetProcAddress (hAdvapi,
+ "GetSecurityDescriptorDacl");
+ GNGetAclInformation =
+ (TGetAclInformation) GetProcAddress (hAdvapi, "GetAclInformation");
+ GNInitializeAcl =
+ (TInitializeAcl) GetProcAddress (hAdvapi, "InitializeAcl");
+ GNGetAce = (TGetAce) GetProcAddress (hAdvapi, "GetAce");
+ GNEqualSid = (TEqualSid) GetProcAddress (hAdvapi, "EqualSid");
+ GNAddAce = (TAddAce) GetProcAddress (hAdvapi, "AddAce");
+ GNAddAccessAllowedAce =
+ (TAddAccessAllowedAce) GetProcAddress (hAdvapi, "AddAccessAllowedAce");
+ GNSetNamedSecurityInfo =
+ (TSetNamedSecurityInfo) GetProcAddress (hAdvapi,
+ "SetNamedSecurityInfoA");
+ }
+ else
+ {
+ GNOpenSCManager = NULL;
+ GNCreateService = NULL;
+ GNCloseServiceHandle = NULL;
+ GNDeleteService = NULL;
+ GNRegisterServiceCtrlHandler = NULL;
+ GNSetServiceStatus = NULL;
+ GNStartServiceCtrlDispatcher = NULL;
+ GNControlService = NULL;
+ GNOpenService = NULL;
+
+ GNLsaOpenPolicy = NULL;
+ GNLsaAddAccountRights = NULL;
+ GNLsaRemoveAccountRights = NULL;
+ GNLsaClose = NULL;
+ GNLookupAccountName = NULL;
+
+ GNGetFileSecurity = NULL;
+ GNInitializeSecurityDescriptor = NULL;
+ GNGetSecurityDescriptorDacl = NULL;
+ GNGetAclInformation = NULL;
+ GNInitializeAcl = NULL;
+ GNGetAce = NULL;
+ GNEqualSid = NULL;
+ GNAddAce = NULL;
+ GNAddAccessAllowedAce = NULL;
+ GNSetNamedSecurityInfo = NULL;
+ }
+
+ /* Account function */
+ hNetapi = LoadLibrary ("netapi32.dll");
+ if (hNetapi)
+ {
+ GNNetUserAdd = (TNetUserAdd) GetProcAddress (hNetapi, "NetUserAdd");
+ GNNetUserSetInfo =
+ (TNetUserSetInfo) GetProcAddress (hNetapi, "NetUserSetInfo");
+ }
+ else
+ {
+ GNNetUserAdd = NULL;
+ GNNetUserSetInfo = NULL;
+ }
+
+ return ret;
+}
+
+/**
+ * Clean up Windows environment
+ */
+void
+GNShutdownWinEnv ()
+{
+ plibc_shutdown ();
+
+#ifdef W32_VEH
+ if (GNWinVEH_handle != NULL)
+ {
+ RemoveVectoredExceptionHandler (GNWinVEH_handle);
+ GNWinVEH_handle = NULL;
+ }
+#endif
+
+ FreeLibrary (hNTDLL);
+ FreeLibrary (hIphlpapi);
+ FreeLibrary (hAdvapi);
+ FreeLibrary (hNetapi);
+
+ CoUninitialize ();
+}
+
+#endif /* MINGW */
+
+#if !HAVE_ATOLL
+long long
+atoll (const char *nptr)
+{
+ return atol (nptr);
+}
+#endif