diff options
Diffstat (limited to 'src/util')
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, ©_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. '<zlib>' 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. '<zlib>' 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 ¤t_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, + ©_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, + ¬_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, ¬i_callback, + ¬iCount); + 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 (¬i_callback, ¬iCount); + 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, ¬ify_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, ¬ify_disconnect, cls); + cfg = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_set_value_number (cfg, "test", "PORT", PORT); + GNUNET_CONFIGURATION_set_value_string (cfg, "test", "HOSTNAME", "localhost"); + 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, ¬ify_ready, NULL); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + + ok = 1; + GNUNET_SCHEDULER_run (&task, NULL); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_server_with_client", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret += check (); + + return ret; +} + +/* end of test_server_with_client.c */ diff --git a/src/util/test_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, ¬ify_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, ¬ify_ready, NULL); +} + + +/** + * Main method, starts scheduler with task1, + * checks that "ok" is correct at the end. + */ +static int +check () +{ + + ok = 1; + GNUNET_SCHEDULER_run (&task, NULL); + return ok; +} + + +int +main (int argc, char *argv[]) +{ + int ret = 0; + + GNUNET_log_setup ("test_server_with_client_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 |