From b80e650bad570e01b5600aab2a667d177fc17770 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Wed, 29 Jul 2009 08:05:52 +0000 Subject: travelhacking --- src/Makefile.am | 2 +- src/arm/arm.h | 2 +- src/arm/gnunet-service-arm.c | 5 +- src/datacache/plugin_datacache_sqlite.c | 5 +- src/datastore/gnunet-service-datastore.c | 3 +- src/fs/Makefile.am | 61 ++ src/fs/fs.h | 128 +++ src/fs/fs_getopt.c | 192 +++++ src/fs/fs_uri.c | 1317 ++++++++++++++++++++++++++++++ src/fs/test_fs_getopt.c | 33 + src/fs/test_fs_uri.c | 255 ++++++ src/hostlist/hostlist-client.c | 11 +- src/hostlist/hostlist-server.c | 5 +- src/include/gnunet_configuration_lib.h | 46 +- src/include/gnunet_container_lib.h | 10 +- src/include/gnunet_testing_lib.h | 43 +- src/testing/Makefile.am | 29 +- src/testing/test_testing.c | 116 +++ src/testing/test_testing_data.conf | 29 + src/testing/testing.c | 186 +++-- src/testing/testing_group.c | 315 +++++-- src/topology/gnunet-daemon-topology.c | 43 +- src/transport/gnunet-service-transport.c | 3 +- src/util/configuration.c | 43 + src/util/container_meta_data.c | 5 +- src/util/crypto_ksk.c | 8 +- src/util/disk.c | 6 +- 27 files changed, 2696 insertions(+), 205 deletions(-) create mode 100644 src/fs/Makefile.am create mode 100644 src/fs/fs.h create mode 100644 src/fs/fs_getopt.c create mode 100644 src/fs/fs_uri.c create mode 100644 src/fs/test_fs_getopt.c create mode 100644 src/fs/test_fs_uri.c create mode 100644 src/testing/test_testing.c create mode 100644 src/testing/test_testing_data.conf (limited to 'src') diff --git a/src/Makefile.am b/src/Makefile.am index 92be62974..9b3aa46ab 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -18,10 +18,10 @@ SUBDIRS = \ statistics \ datacache \ datastore \ - testing \ template \ transport \ core \ + testing \ $(HOSTLIST_DIR) \ topology diff --git a/src/arm/arm.h b/src/arm/arm.h index 2293ec036..a8f5d0706 100644 --- a/src/arm/arm.h +++ b/src/arm/arm.h @@ -27,6 +27,6 @@ #include "gnunet_common.h" -#define DEBUG_ARM GNUNET_NO +#define DEBUG_ARM GNUNET_YES #endif diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 32674117c..ac58904ed 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.c @@ -167,6 +167,8 @@ signal_result (struct GNUNET_SERVER_Client *client, { uint16_t *res; + if (NULL == client) + return; #if DEBUG_ARM GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Telling client that service `%s' is now %s\n", @@ -387,7 +389,8 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename) sl->mtime = sbuf.st_mtime; running = sl; start_process (sl); - signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); + if (NULL != client) + signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); } diff --git a/src/datacache/plugin_datacache_sqlite.c b/src/datacache/plugin_datacache_sqlite.c index 63f8cbd67..e8ef5cd09 100644 --- a/src/datacache/plugin_datacache_sqlite.c +++ b/src/datacache/plugin_datacache_sqlite.c @@ -405,7 +405,10 @@ libgnunet_plugin_datacache_sqlite_done (void *cls) struct GNUNET_DATACACHE_PluginFunctions *api = cls; struct Plugin *plugin = api->cls; - UNLINK (plugin->fn); + if (0 != UNLINK (plugin->fn)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "unlink", + plugin->fn); GNUNET_free (plugin->fn); sqlite3_close (plugin->dbh); GNUNET_free (plugin); diff --git a/src/datastore/gnunet-service-datastore.c b/src/datastore/gnunet-service-datastore.c index 26e894bf4..977f1398e 100644 --- a/src/datastore/gnunet-service-datastore.c +++ b/src/datastore/gnunet-service-datastore.c @@ -527,7 +527,8 @@ transmit_status (struct GNUNET_SERVER_Client *client, sm->header.size = htons(sizeof(struct StatusMessage) + slen); sm->header.type = htons(GNUNET_MESSAGE_TYPE_DATASTORE_STATUS); sm->status = htonl(code); - memcpy (&sm[1], msg, slen); + if (slen > 0) + memcpy (&sm[1], msg, slen); transmit (client, &sm->header, NULL, NULL, GNUNET_YES); } diff --git a/src/fs/Makefile.am b/src/fs/Makefile.am new file mode 100644 index 000000000..03c40a5da --- /dev/null +++ b/src/fs/Makefile.am @@ -0,0 +1,61 @@ +INCLUDES = -I$(top_srcdir)/src/include + +if MINGW + WINFLAGS = -Wl,--no-undefined -Wl,--export-all-symbols +endif + +if USE_COVERAGE + AM_CFLAGS = --coverage -O0 + XLIB = -lgcov +endif + + +lib_LTLIBRARIES = libgnunetfs.la + +libgnunetfs_la_SOURCES = \ + fs_getopt.c \ + fs_uri.c +libgnunetfs_la_LIBADD = \ + $(top_builddir)/src/util/libgnunetutil.la \ + $(GN_LIBINTL) $(XLIB) +libgnunetfs_la_LDFLAGS = \ + $(GN_LIB_LDFLAGS) $(WINFLAGS) \ + -version-info 0:0:0 + + +#bin_PROGRAMS = \ +# gnunet-directory \ +# gnunet-download \ +# gnunet-pseudonym \ +# gnunet-search \ +# gnunet-share \ +# gnunet-unindex + +#gnunet_directory_SOURCES = \ +# gnunet-directory.c +#gnunet_directory_LDADD = \ +# $(top_builddir)/src/fs/libgnunetfs.la \ +# $(top_builddir)/src/util/libgnunetutil.la \ +# $(GN_LIBINTL) + + +check_PROGRAMS = \ + test_fs_getopt \ + test_fs_uri + +TESTS = $(check_PROGRAMS) + +test_fs_uri_SOURCES = \ + test_fs_uri.c +test_fs_LDADD = \ + $(top_builddir)/src/fs/libgnunetfs.la \ + $(top_builddir)/src/util/libgnunetutil.la + +test_fs_getopt_SOURCES = \ + test_fs_getopt.c +test_fs_LDADD = \ + $(top_builddir)/src/fs/libgnunetfs.la \ + $(top_builddir)/src/util/libgnunetutil.la + +#EXTRA_DIST = \ +# test_fs_data.conf diff --git a/src/fs/fs.h b/src/fs/fs.h new file mode 100644 index 000000000..c6d5d38cc --- /dev/null +++ b/src/fs/fs.h @@ -0,0 +1,128 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file fs/fs.h + * @brief definitions for the entire fs module + * @author Igor Wronsky, Christian Grothoff + */ +#ifndef FS_H +#define FS_H + +/** + * @brief content hash key + */ +struct ContentHashKey +{ + GNUNET_HashCode key; + GNUNET_HashCode query; +}; + + +/** + * @brief complete information needed + * to download a file. + */ +struct FileIdentifier +{ + + /** + * Total size of the file in bytes. (network byte order (!)) + */ + unsigned long long file_length; + + /** + * Query and key of the top GNUNET_EC_IBlock. + */ + struct ContentHashKey chk; + +}; + + +/** + * Information about a file and its location + * (peer claiming to share the file). + */ +struct Location +{ + /** + * Information about the shared file. + */ + struct FileIdentifier fi; + + /** + * Identity of the peer sharing the file. + */ + struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded peer; + + /** + * Time when the HELLO *and* this location URI + * expire (they expire together!). + */ + struct GNUNET_TIME_Absolute expirationTime; + + /** + * RSA signature over the GNUNET_EC_FileIdentifier, + * GNUNET_hash of the peer and expiration time. + */ + struct GNUNET_CRYPTO_RsaSignature contentSignature; + +}; + +enum uri_types +{ chk, sks, ksk, loc }; + +/** + * A Universal Resource Identifier (URI), opaque. + */ +struct GNUNET_FS_Uri +{ + enum uri_types type; + union + { + struct + { + /** + * Keywords start with a '+' if they are + * mandatory (in which case the '+' is NOT + * part of the keyword) and with a + * simple space if they are optional + * (in which case the space is ALSO not + * part of the actual keyword). + * + * Double-quotes to protect spaces and + * %-encoding are NOT used internally + * (only in URI-strings). + */ + char **keywords; + unsigned int keywordCount; + } ksk; + struct + { + GNUNET_HashCode namespace; + char *identifier; + } sks; + struct FileIdentifier chk; + struct Location loc; + } data; + +}; + +#endif diff --git a/src/fs/fs_getopt.c b/src/fs/fs_getopt.c new file mode 100644 index 000000000..8afe49852 --- /dev/null +++ b/src/fs/fs_getopt.c @@ -0,0 +1,192 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file fs/fs_getopt.c + * @brief helper functions for command-line argument processing + * @author Igor Wronsky, Christian Grothoff + */ +#include "platform.h" +#include "gnunet_fs_lib.h" + + + +/* ******************** command-line option parsing API *********************** */ + +/** + * Command-line option parser function that allows the user + * to specify one or more '-k' options with keywords. Each + * specified keyword will be added to the URI. A pointer to + * the URI must be passed as the "scls" argument. + * + * @param ctx command line processor context + * @param scls must be of type "struct GNUNET_FS_Uri **" + * @param option name of the option (typically 'k') + * @param value command line argument given + * @return GNUNET_OK on success + */ +int +GNUNET_FS_getopt_configure_set_keywords (GNUNET_GETOPT_CommandLineProcessorContext* ctx, + void *scls, + const char *option, + const char *value) +{ + struct GNUNET_FS_Uri **uri = scls; + struct GNUNET_FS_Uri *u = *uri; + char *val; + size_t slen; + + if (u == NULL) + { + u = GNUNET_malloc (sizeof (struct GNUNET_ECRS_URI)); + *uri = u; + u->type = ksk; + u->data.ksk.keywordCount = 0; + u->data.ksk.keywords = NULL; + } + else + { + GNUNET_assert (u->type == ksk); + } + slen = strlen (value); + if (slen == 0) + return GNUNET_SYSERR; /* cannot be empty */ + if (value[0] == '+') + { + /* simply preserve the "mandatory" flag */ + if (slen < 2) + return GNUNET_SYSERR; /* empty keywords not allowed */ + if ((value[1] == '"') && (slen > 3) && (value[slen - 1] == '"')) + { + /* remove the quotes, keep the '+' */ + val = GNUNET_malloc (slen - 1); + val[0] = '+'; + memcpy (&val[1], &value[2], slen - 3); + val[slen - 2] = '\0'; + } + else + { + /* no quotes, just keep the '+' */ + val = GNUNET_strdup (value); + } + } + else + { + if ((value[0] == '"') && (slen > 2) && (value[slen - 1] == '"')) + { + /* remove the quotes, add a space */ + val = GNUNET_malloc (slen); + val[0] = ' '; + memcpy (&val[1], &value[1], slen - 2); + val[slen - 1] = '\0'; + } + else + { + /* add a space to indicate "not mandatory" */ + val = GNUNET_malloc (slen + 2); + strcpy (val, " "); + strcat (val, value); + } + } + GNUNET_array_grow (u->data.ksk.keywords, + u->data.ksk.keywordCount, u->data.ksk.keywordCount + 1); + u->data.ksk.keywords[u->data.ksk.keywordCount - 1] = val; + return GNUNET_OK; +} + + +/** + * Command-line option parser function that allows the user to specify + * one or more '-m' options with metadata. Each specified entry of + * the form "type=value" will be added to the metadata. A pointer to + * the metadata must be passed as the "scls" argument. + * + * @param ctx command line processor context + * @param scls must be of type "struct GNUNET_MetaData **" + * @param option name of the option (typically 'k') + * @param value command line argument given + * @return GNUNET_OK on success + */ +int +GNUNET_FS_getopt_configure_set_metadata (GNUNET_GETOPT_CommandLineProcessorContext* ctx, + void *scls, + const char *option, + const char *value) + +{ + struct GNUNET_CONTAINER_MetaData **mm = scls; + EXTRACTOR_KeywordType type; + const char *typename; + const char *typename_i18n; + struct GNUNET_CONTAINER_MetaData *meta; + char *tmp; + + meta = *mm; + if (meta == NULL) + { + meta = GNUNET_CONTAINER_meta_data_create (); + *mm = meta; + } + + tmp = GNUNET_STRINGS_to_utf8 (NULL, value, strlen (value), +#if ENABLE_NLS + nl_langinfo (CODESET) +#else + "utf-8" +#endif + ); + type = EXTRACTOR_getHighestKeywordTypeNumber (); + while (type > 0) + { + type--; + typename = EXTRACTOR_getKeywordTypeAsString (type); + typename_i18n = dgettext (LIBEXTRACTOR_GETTEXT_DOMAIN, typename); + if ((strlen (tmp) >= strlen (typename) + 1) && + (tmp[strlen (typename)] == ':') && + (0 == strncmp (typename, tmp, strlen (typename)))) + { + GNUNET_CONTAINER_meta_data_insert (meta, type, &tmp[strlen (typename) + 1]); + GNUNET_free (tmp); + tmp = NULL; + break; + } + if ((strlen (tmp) >= strlen (typename_i18n) + 1) && + (tmp[strlen (typename_i18n)] == ':') && + (0 == strncmp (typename_i18n, tmp, strlen (typename_i18n)))) + { + GNUNET_CONTAINER_meta_data_insert (meta, type, + &tmp[strlen (typename_i18n) + 1]); + GNUNET_free (tmp); + tmp = NULL; + break; + } + } + if (tmp != NULL) + { + GNUNET_CONTAINER_meta_data_insert (meta, EXTRACTOR_UNKNOWN, tmp); + GNUNET_free (tmp); + printf (_ + ("Unknown metadata type in metadata option `%s'. Using metadata type `unknown' instead.\n"), + value); + } + return GNUNET_OK; +} + +/* end of fs_getopt.c */ diff --git a/src/fs/fs_uri.c b/src/fs/fs_uri.c new file mode 100644 index 000000000..863ab475d --- /dev/null +++ b/src/fs/fs_uri.c @@ -0,0 +1,1317 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file fs/fs_uri.c + * @brief Parses and produces uri strings. + * @author Igor Wronsky, Christian Grothoff + * + * GNUnet URIs are of the general form "gnunet://MODULE/IDENTIFIER". + * The specific structure of "IDENTIFIER" depends on the module and + * maybe differenciated into additional subcategories if applicable. + * This module only deals with ecrs identifiers (MODULE = "ecrs"). + *

+ * + * This module only parses URIs for the AFS module. The ECRS URIs fall + * into four categories, "chk", "sks", "ksk" and "loc". The first three + * categories were named in analogy (!) to Freenet, but they do NOT + * work in exactly the same way. They are very similar from the user's + * point of view (unique file identifier, subspace, keyword), but the + * implementation is rather different in pretty much every detail. + * The concrete URI formats are: + * + *

+ * + * The encoding for hexadecimal values is defined in the hashing.c + * module in the gnunetutil library and discussed there. + *

+ */ +#include "platform.h" +#include "gnunet_fs_lib.h" +#include "fs.h" + + +/** + * Get a unique key from a URI. This is for putting URIs + * into HashMaps. The key may change between FS implementations. + * + * @param uri uri to convert to a unique key + * @param key wherer to store the unique key + */ +void +GNUNET_FS_uri_to_key (const struct GNUNET_FS_Uri *uri, + GNUNET_HashCode * key) +{ + switch (uri->type) + { + case chk: + *key = uri->data.fi.chk.query; + return; + case sks: + GNUNET_hash (uri->data.sks.identifier, + strlen (uri->data.sks.identifier), key); + break; + case ksk: + if (uri->data.ksk.keywordCount > 0) + GNUNET_hash (uri->data.ksk.keywords[0], + strlen (uri->data.ksk.keywords[0]), key); + break; + case loc: + GNUNET_hash (&uri->data.loc.fi, + sizeof (GNUNET_EC_FileIdentifier) + + sizeof (GNUNET_RSA_PublicKey), key); + break; + default: + memset (key, 0, sizeof (GNUNET_HashCode)); + break; + } +} + + +/** + * Convert a URI to a UTF-8 String. + * + * @param uri uri to convert to a string + * @return the UTF-8 string + */ +char * +GNUNET_FS_uri_to_string (const struct GNUNET_FS_Uri *uri); + + +/** + * Convert keyword URI to a human readable format + * (i.e. the search query that was used in the first place) + * + * @param uri ksk uri to convert to a string + * @return string with the keywords + */ +char * +GNUNET_FS_uri_ksk_to_string_fancy (const struct GNUNET_FS_Uri *uri); + +/** + * Convert a UTF-8 String to a URI. + * + * @param uri string to parse + * @param emsg where to store the parser error message (if any) + * @return NULL on error + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_parse (const char *uri, + char **emsg); + +/** + * Free URI. + * + * @param uri uri to free + */ +void +GNUNET_FS_uri_destroy (struct GNUNET_FS_Uri *uri) +{ + unsigned int i; + + GNUNET_assert (uri != NULL); + switch (uri->type) + { + case ksk: + for (i = 0; i < uri->data.ksk.keywordCount; i++) + GNUNET_free (uri->data.ksk.keywords[i]); + GNUNET_array_grow (uri->data.ksk.keywords, uri->data.ksk.keywordCount, + 0); + break; + case sks: + GNUNET_free (uri->data.sks.identifier); + break; + case loc: + break; + default: + /* do nothing */ + break; + } + GNUNET_free (uri); +} + +/** + * How many keywords are ANDed in this keyword URI? + * + * @param uri ksk uri to get the number of keywords from + * @return 0 if this is not a keyword URI + */ +unsigned int +GNUNET_FS_uri_ksk_get_keyword_count (const struct GNUNET_FS_Uri *uri) +{ + if (uri->type != ksk) + return 0; + return uri->data.ksk.keywordCount; +} + + +/** + * Iterate over all keywords in this keyword URI. + * + * @param uri ksk uri to get the keywords from + * @param iterator function to call on each keyword + * @param iterator_cls closure for iterator + * @return -1 if this is not a keyword URI, otherwise number of + * keywords iterated over until iterator aborted + */ +int +GNUNET_FS_uri_ksk_get_keywords (const struct GNUNET_FS_Uri *uri, + GNUNET_FS_KeywordIterator iterator, + void *iterator_cls) +{ + unsigned int i; + char *keyword; + + if (uri->type != ksk) + return -1; + if (iterator == NULL) + return uri->data.ksk.keywordCount; + for (i = 0; i < uri->data.ksk.keywordCount; i++) + { + keyword = uri->data.ksk.keywords[i]; + /* first character of keyword indicates + if it is mandatory or not */ + if (GNUNET_OK != iterator (&keyword[1], keyword[0] == '+', cls)) + return i; + } + return i; +} + + +/** + * Obtain the identity of the peer offering the data + * + * @param uri the location URI to inspect + * @param peer where to store the identify of the peer (presumably) offering the content + * @return GNUNET_SYSERR if this is not a location URI, otherwise GNUNET_OK + */ +int +GNUNET_FS_uri_loc_get_peer_identity (const struct GNUNET_FS_Uri *uri, + struct GNUNET_PeerIdentity * peer) +{ + if (uri->type != loc) + return GNUNET_SYSERR; + GNUNET_hash (&uri->data.loc.peer, sizeof (GNUNET_RSA_PublicKey), + &peer->hashPubKey); + return GNUNET_OK; +} + + +/** + * Obtain the URI of the content itself. + * + * @param uri location URI to get the content URI from + * @return NULL if argument is not a location URI + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_loc_get_uri (const struct GNUNET_FS_Uri *uri) +{ + struct GNUNET_ECRS_Uri *ret; + + if (uri->type != loc) + return NULL; + ret = GNUNET_malloc (sizeof (struct GNUNET_ECRS_Uri)); + ret->type = chk; + ret->data.chk = uri->data.loc.fi; + return ret; +} + + +/** + * Construct a location URI (this peer will be used for the location). + * + * @param baseURI content offered by the sender + * @param cfg configuration information (used to find our hostkey) + * @param expiration_time how long will the content be offered? + * @return the location URI, NULL on error + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_loc_create (const struct GNUNET_FS_Uri *baseUri, + struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TIME_Absolute expiration_time); + + +/** + * Canonicalize keyword URI. Performs operations such + * as decapitalization and removal of certain characters. + * (useful for search). + * + * @param uri the URI to canonicalize + * @return canonicalized version of the URI, NULL on error + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_ksk_canonicalize (const struct GNUNET_FS_Uri *uri); + + +/** + * Merge the sets of keywords from two KSK URIs. + * (useful for merging the canonicalized keywords with + * the original keywords for sharing). + * + * @param u1 first uri + * @param u2 second uri + * @return merged URI, NULL on error + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_ksk_merge (const struct GNUNET_FS_Uri *u1, + const struct GNUNET_FS_Uri *u2); + + +/** + * Duplicate URI. + * + * @param uri the URI to duplicate + * @return copy of the URI + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_dup (const struct GNUNET_FS_Uri *uri) +{ + struct GNUNET_ECRS_URI *ret; + unsigned int i; + + ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri)); + memcpy (ret, uri, sizeof (struct GNUNET_FS_Uri)); + switch (ret->type) + { + case ksk: + if (ret->data.ksk.keywordCount > 0) + { + ret->data.ksk.keywords + = GNUNET_malloc (ret->data.ksk.keywordCount * sizeof (char *)); + for (i = 0; i < ret->data.ksk.keywordCount; i++) + ret->data.ksk.keywords[i] = + GNUNET_strdup (uri->data.ksk.keywords[i]); + } + else + ret->data.ksk.keywords = NULL; /* just to be sure */ + break; + case sks: + ret->data.sks.identifier = GNUNET_strdup (uri->data.sks.identifier); + break; + case loc: + break; + default: + break; + } + return ret; +} + + +/** + * Create an FS URI from a single user-supplied string of keywords. + * The string is broken up at spaces into individual keywords. + * Keywords that start with "+" are mandatory. Double-quotes can + * be used to prevent breaking up strings at spaces (and also + * to specify non-mandatory keywords starting with "+"). + * + * Keywords must contain a balanced number of double quotes and + * double quotes can not be used in the actual keywords (for + * example, the string '""foo bar""' will be turned into two + * "OR"ed keywords 'foo' and 'bar', not into '"foo bar"'. + * + * @param keywords the keyword string + * @return an FS URI for the given keywords, NULL + * if keywords is not legal (i.e. empty). + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_ksk_create (const char *keywords); + + +/** + * Create an FS URI from a user-supplied command line of keywords. + * Arguments should start with "+" to indicate mandatory + * keywords. + * + * @param argc number of keywords + * @param argv keywords (double quotes are not required for + * keywords containing spaces; however, double + * quotes are required for keywords starting with + * "+"); there is no mechanism for having double + * quotes in the actual keywords (if the user + * did specifically specify double quotes, the + * caller should convert each double quote + * into two single quotes). + * @return an FS URI for the given keywords, NULL + * if keywords is not legal (i.e. empty). + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_ksk_create_from_args (unsigned int argc, + const char **argv); + + +/** + * Test if two URIs are equal. + * + * @param u1 one of the URIs + * @param u2 the other URI + * @return GNUNET_YES if the URIs are equal + */ +int +GNUNET_FS_uri_test_equal (const struct GNUNET_FS_Uri *u1, + const struct GNUNET_FS_Uri *u2) +{ + int ret; + unsigned int i; + unsigned int j; + + GNUNET_assert (uri1 != NULL); + GNUNET_assert (uri2 != NULL); + if (uri1->type != uri2->type) + return GNUNET_NO; + switch (uri1->type) + { + case chk: + if (0 == memcmp (&uri1->data.chk, + &uri2->data.chk, + sizeof (struct FileIdentifier))) + return GNUNET_YES; + return GNUNET_NO; + case sks: + if ((0 == memcmp (&uri1->data.sks.namespace, + &uri2->data.sks.namespace, + sizeof (GNUNET_HashCode))) && + (0 == strcmp (uri1->data.sks.identifier, + uri2->data.sks.identifier))) + + return GNUNET_YES; + return GNUNET_NO; + case ksk: + if (uri1->data.ksk.keywordCount != uri2->data.ksk.keywordCount) + return GNUNET_NO; + for (i = 0; i < uri1->data.ksk.keywordCount; i++) + { + ret = GNUNET_NO; + for (j = 0; j < uri2->data.ksk.keywordCount; j++) + { + if (0 == strcmp (uri1->data.ksk.keywords[i], + uri2->data.ksk.keywords[j])) + { + ret = GNUNET_YES; + break; + } + } + if (ret == GNUNET_NO) + return GNUNET_NO; + } + return GNUNET_YES; + case loc: + if (memcmp (&uri1->data.loc, + &uri2->data.loc, + sizeof (struct FileIdentifier) + + sizeof (GNUNET_RSA_PublicKey) + + sizeof (struct GNUNET_TIME_Absolute) + + sizeof (unsigned short) + sizeof (unsigned short)) != 0) + return GNUNET_NO; + return GNUNET_YES; + default: + return GNUNET_NO; + } +} + + +/** + * Is this a namespace URI? + * + * @param uri the uri to check + * @return GNUNET_YES if this is an SKS uri + */ +int +GNUNET_FS_uri_test_sks (const struct GNUNET_FS_Uri *uri) +{ + return uri->type == sks; +} + + +/** + * Get the ID of a namespace from the given + * namespace URI. + * + * @param uri the uri to get the namespace ID from + * @param nsid where to store the ID of the namespace + * @return GNUNET_OK on success + */ +int +GNUNET_FS_uri_sks_get_namespace (const struct GNUNET_FS_Uri *uri, + GNUNET_HashCode * nsid) +{ + if (! GNUNET_FS_uri_test_sks (uri)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + *id = uri->data.sks.namespace; + return GNUNET_OK; +} + + +/** + * Get the content identifier of an SKS URI. + * + * @param uri the sks uri + * @return NULL on error (not a valid SKS URI) + */ +char * +GNUNET_FS_uri_sks_get_content_id (const struct GNUNET_FS_Uri *uri) +{ + if (!GNUNET_FS_uri_test_sks (uri)) + { + GNUNET_break (0); + return NULL; + } + return GNUNET_strdup (uri->data.sks.identifier); +} + + +/** + * Convert namespace URI to a human readable format + * (using the namespace description, if available). + * + * @param cfg configuration to use + * @param uri SKS uri to convert + * @return NULL on error (not an SKS URI) + */ +char * +GNUNET_FS_uri_sks_to_string_fancy (struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_FS_Uri *uri); + + +/** + * Is this a keyword URI? + * + * @param uri the uri + * @return GNUNET_YES if this is a KSK uri + */ +int +GNUNET_FS_uri_test_ksk (const struct GNUNET_FS_Uri *uri) +{ +#if EXTRA_CHECKS + unsigned int i; + + if (uri->type == ksk) + { + for (i = uri->data.ksk.keywordCount - 1; i >= 0; i--) + GNUNET_assert (uri->data.ksk.keywords[i] != NULL); + } +#endif + return uri->type == ksk; +} + + +/** + * Is this a file (or directory) URI? + * + * @param uri the uri to check + * @return GNUNET_YES if this is a CHK uri + */ +int +GNUNET_FS_uri_test_chk (const struct GNUNET_FS_Uri *uri) +{ + return uri->type == chk; +} + + +/** + * What is the size of the file that this URI + * refers to? + * + * @param uri the CHK URI to inspect + * @return size of the file as specified in the CHK URI + */ +uint64_t +GNUNET_FS_uri_chk_get_file_size (const struct GNUNET_FS_Uri *uri) +{ + switch (uri->type) + { + case chk: + return GNUNET_ntohll (uri->data.chk.file_length); + case loc: + return GNUNET_ntohll (uri->data.loc.fi.file_length); + default: + GNUNET_assert (0); + } + return 0; /* unreachable */ +} + + +/** + * Is this a location URI? + * + * @param uri the uri to check + * @return GNUNET_YES if this is a LOC uri + */ +int +GNUNET_FS_uri_test_loc (const struct GNUNET_FS_Uri *uri) +{ + return uri->type == loc; +} + + +/** + * Function called on each value in the meta data. + * Adds it to the URI. + * + * @param cls URI to update + * @param type type of the meta data + * @param data value of the meta data + * @return GNUNET_OK (always) + */ +static int +gather_uri_data (void *cls, + EXTRACTOR_KeywordType type, + const char *data) +{ + struct GNUNET_FS_Uri *uri = cls; + char *nkword; + int j; + + for (j = uri->data.ksk.keywordCount - 1; j >= 0; j--) + if (0 == strcmp (&uri->data.ksk.keywords[j][1], data)) + return GNUNET_OK; + nkword = GNUNET_malloc (strlen (data) + 2); + strcpy (nkword, " "); /* not mandatory */ + strcat (nkword, data); + uri->data.ksk.keywords[uri->data.ksk.keywordCount++] = nkword; + return GNUNET_OK; +} + + +/** + * Construct a keyword-URI from meta-data (take all entries + * in the meta-data and construct one large keyword URI + * that lists all keywords that can be found in the meta-data). + * @deprecated + */ +struct GNUNET_FS_Uri * +GNUNET_FS_uri_ksk_create_from_meta_data (const struct GNUNET_MetaData *md) +{ + struct GNUNET_FS_Uri *ret; + + if (md == NULL) + return NULL; + ret = GNUNET_malloc (sizeof (struct GNUNET_FS_Uri)); + ret->type = ksk; + ret->data.ksk.keywordCount = 0; + ret->data.ksk.keywords = NULL; + ret->data.ksk.keywords + = GNUNET_malloc (sizeof (char *) * + GNUNET_meta_data_get_contents (md, NULL, NULL)); + GNUNET_meta_data_get_contents (md, &gather_uri_data, ret); + return ret; + +} + +#if 0 + +// old code... + + + +/** + * In URI-encoding, does the given character + * need to be encoded using %-encoding? + */ +static int +needs_percent (char c) +{ + return (!((isalnum (c)) || + (c == '-') || (c == '_') || (c == '.') || (c == '~'))); +} + +/** + * Generate a keyword URI. + * @return NULL on error (i.e. keywordCount == 0) + */ +static char * +createKeywordURI (char **keywords, unsigned int keywordCount) +{ + size_t n; + char *ret; + unsigned int i; + unsigned int j; + unsigned int wpos; + size_t slen; + const char *keyword; + + n = + keywordCount + strlen (GNUNET_ECRS_URI_PREFIX) + + strlen (GNUNET_ECRS_SEARCH_INFIX) + 1; + for (i = 0; i < keywordCount; i++) + { + keyword = keywords[i]; + slen = strlen (keyword); + n += slen; + for (j = 0; j < slen; j++) + { + if ((j == 0) && (keyword[j] == ' ')) + { + n--; + continue; /* skip leading space */ + } + if (needs_percent (keyword[j])) + n += 2; /* will use %-encoding */ + } + } + ret = GNUNET_malloc (n); + strcpy (ret, GNUNET_ECRS_URI_PREFIX); + strcat (ret, GNUNET_ECRS_SEARCH_INFIX); + wpos = strlen (ret); + for (i = 0; i < keywordCount; i++) + { + keyword = keywords[i]; + slen = strlen (keyword); + for (j = 0; j < slen; j++) + { + if ((j == 0) && (keyword[j] == ' ')) + continue; /* skip leading space */ + if (needs_percent (keyword[j])) + { + sprintf (&ret[wpos], "%%%02X", keyword[j]); + wpos += 3; + } + else + { + ret[wpos++] = keyword[j]; + } + } + if (i != keywordCount - 1) + ret[wpos++] = '+'; + } + return ret; +} + +/** + * Generate a subspace URI. + */ +static char * +createSubspaceURI (const GNUNET_HashCode * namespace, const char *identifier) +{ + size_t n; + char *ret; + GNUNET_EncName ns; + + n = + sizeof (GNUNET_EncName) + strlen (GNUNET_ECRS_URI_PREFIX) + + strlen (GNUNET_ECRS_SUBSPACE_INFIX) + 1 + strlen (identifier); + ret = GNUNET_malloc (n); + GNUNET_hash_to_enc (namespace, &ns); + GNUNET_snprintf (ret, n, + "%s%s%s/%s", + GNUNET_ECRS_URI_PREFIX, GNUNET_ECRS_SUBSPACE_INFIX, + (const char *) &ns, identifier); + return ret; +} + +/** + * Generate a file URI. + */ +static char * +createFileURI (const GNUNET_EC_FileIdentifier * fi) +{ + char *ret; + GNUNET_EncName keyhash; + GNUNET_EncName queryhash; + size_t n; + + GNUNET_hash_to_enc (&fi->chk.key, &keyhash); + GNUNET_hash_to_enc (&fi->chk.query, &queryhash); + + n = + strlen (GNUNET_ECRS_URI_PREFIX) + 2 * sizeof (GNUNET_EncName) + 8 + 16 + + 32 + strlen (GNUNET_ECRS_FILE_INFIX); + ret = GNUNET_malloc (n); + GNUNET_snprintf (ret, + n, + "%s%s%s.%s.%llu", + GNUNET_ECRS_URI_PREFIX, + GNUNET_ECRS_FILE_INFIX, + (char *) &keyhash, (char *) &queryhash, + GNUNET_ntohll (fi->file_length)); + return ret; +} + +#include "bincoder.c" + +/** + * Create a (string) location URI from a Location. + */ +static char * +createLocURI (const Location * loc) +{ + size_t n; + char *ret; + GNUNET_EncName keyhash; + GNUNET_EncName queryhash; + char *peerId; + char *peerSig; + + GNUNET_hash_to_enc (&loc->fi.chk.key, &keyhash); + GNUNET_hash_to_enc (&loc->fi.chk.query, &queryhash); + n = 2148; + peerId = bin2enc (&loc->peer, sizeof (GNUNET_RSA_PublicKey)); + peerSig = bin2enc (&loc->contentSignature, sizeof (GNUNET_RSA_Signature)); + ret = GNUNET_malloc (n); + GNUNET_snprintf (ret, + n, + "%s%s%s.%s.%llu.%s.%s.%u", + GNUNET_ECRS_URI_PREFIX, + GNUNET_ECRS_LOCATION_INFIX, + (char *) &keyhash, + (char *) &queryhash, + GNUNET_ntohll (loc->fi.file_length), + peerId, peerSig, loc->expirationTime); + GNUNET_free (peerSig); + GNUNET_free (peerId); + return ret; +} + +/** + * Convert a URI to a UTF-8 String. + */ +char * +GNUNET_ECRS_uri_to_string (const struct GNUNET_ECRS_URI *uri) +{ + if (uri == NULL) + { + GNUNET_GE_BREAK (NULL, 0); + return NULL; + } + switch (uri->type) + { + case ksk: + return createKeywordURI (uri->data.ksk.keywords, + uri->data.ksk.keywordCount); + case sks: + return createSubspaceURI (&uri->data.sks.namespace, + uri->data.sks.identifier); + case chk: + return createFileURI (&uri->data.fi); + case loc: + return createLocURI (&uri->data.loc); + default: + GNUNET_GE_BREAK (NULL, 0); + return NULL; + } +} + +/** + * Convert keyword URI to a human readable format + * (i.e. the search query that was used in the first place) + */ +char * +GNUNET_ECRS_ksk_uri_to_human_readable_string (const struct GNUNET_ECRS_URI + *uri) +{ + size_t n; + char *ret; + unsigned int i; + const char *keyword; + char **keywords; + unsigned int keywordCount; + + if ((uri == NULL) || (uri->type != ksk)) + { + GNUNET_GE_BREAK (NULL, 0); + return NULL; + } + keywords = uri->data.ksk.keywords; + keywordCount = uri->data.ksk.keywordCount; + n = keywordCount + 1; + for (i = 0; i < keywordCount; i++) + { + keyword = keywords[i]; + n += strlen (keyword) - 1; + if (NULL != strstr (&keyword[1], " ")) + n += 2; + if (keyword[0] == '+') + n++; + } + ret = GNUNET_malloc (n); + strcpy (ret, ""); + for (i = 0; i < keywordCount; i++) + { + keyword = keywords[i]; + if (NULL != strstr (&keyword[1], " ")) + { + strcat (ret, "\""); + if (keyword[0] == '+') + strcat (ret, keyword); + else + strcat (ret, &keyword[1]); + strcat (ret, "\""); + } + else + { + if (keyword[0] == '+') + strcat (ret, keyword); + else + strcat (ret, &keyword[1]); + } + strcat (ret, " "); + } + return ret; +} + +/** + * Given a keyword with %-encoding (and possibly quotes to protect + * spaces), return a copy of the keyword without %-encoding and + * without double-quotes (%22). Also, add a space at the beginning + * if there is not a '+'. + */ +static char * +percent_decode_keyword (const char *in) +{ + char *out; + char *ret; + unsigned int rpos; + unsigned int wpos; + unsigned int hx; + + out = GNUNET_strdup (in); + rpos = 0; + wpos = 0; + while (out[rpos] != '\0') + { + if (out[rpos] == '%') + { + if (1 != sscanf (&out[rpos + 1], "%2X", &hx)) + { + GNUNET_free (out); + return NULL; + } + rpos += 3; + if (hx == '"') + continue; /* skip double quote */ + out[wpos++] = (char) hx; + } + else + { + out[wpos++] = out[rpos++]; + } + } + out[wpos] = '\0'; + if (out[0] == '+') + { + ret = GNUNET_strdup (out); + } + else + { + /* need to prefix with space */ + ret = GNUNET_malloc (strlen (out) + 2); + strcpy (ret, " "); + strcat (ret, out); + } + GNUNET_free (out); + return ret; +} + +/** + * Parses an ECRS search URI. + * + * @param uri an uri string + * @param keyword will be set to an array with the keywords + * @return GNUNET_SYSERR if this is not a search URI, otherwise + * the number of keywords placed in the array + */ +static int +parseKeywordURI (struct GNUNET_GE_Context *ectx, const char *uri, + char ***keywords) +{ + unsigned int pos; + int ret; + int iret; + int i; + size_t slen; + char *dup; + int saw_quote; + + GNUNET_GE_ASSERT (ectx, uri != NULL); + + slen = strlen (uri); + pos = strlen (GNUNET_ECRS_URI_PREFIX); + + if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) + return GNUNET_SYSERR; + if (0 != + strncmp (&uri[pos], GNUNET_ECRS_SEARCH_INFIX, + strlen (GNUNET_ECRS_SEARCH_INFIX))) + return GNUNET_SYSERR; + pos += strlen (GNUNET_ECRS_SEARCH_INFIX); + if (slen == pos) + { + /* no keywords */ + (*keywords) = NULL; + return 0; + } + if ((uri[slen - 1] == '+') || (uri[pos] == '+')) + return GNUNET_SYSERR; /* no keywords / malformed */ + + ret = 1; + saw_quote = 0; + for (i = pos; i < slen; i++) + { + if ((uri[i] == '%') && (&uri[i] == strstr (&uri[i], "%22"))) + { + saw_quote = (saw_quote + 1) % 2; + i += 3; + continue; + } + if ((uri[i] == '+') && (saw_quote == 0)) + { + ret++; + if (uri[i - 1] == '+') + return GNUNET_SYSERR; /* "++" not allowed */ + } + } + if (saw_quote == 1) + return GNUNET_SYSERR; /* quotes not balanced */ + iret = ret; + dup = GNUNET_strdup (uri); + (*keywords) = GNUNET_malloc (ret * sizeof (char *)); + for (i = 0; i < ret; i++) + (*keywords)[i] = NULL; + for (i = slen - 1; i >= pos; i--) + { + if ((uri[i] == '%') && (&uri[i] == strstr (&uri[i], "%22"))) + { + saw_quote = (saw_quote + 1) % 2; + i += 3; + continue; + } + if ((dup[i] == '+') && (saw_quote == 0)) + { + (*keywords)[--ret] = percent_decode_keyword (&dup[i + 1]); + if (NULL == (*keywords)[ret]) + goto CLEANUP; + dup[i] = '\0'; + } + } + (*keywords)[--ret] = percent_decode_keyword (&dup[pos]); + if (NULL == (*keywords)[ret]) + goto CLEANUP; + GNUNET_GE_ASSERT (ectx, ret == 0); + GNUNET_free (dup); + return iret; +CLEANUP: + for (i = 0; i < ret; i++) + GNUNET_free_non_null ((*keywords)[i]); + GNUNET_free (*keywords); + *keywords = NULL; + GNUNET_free (dup); + return GNUNET_SYSERR; +} + +/** + * Parses an AFS namespace / subspace identifier URI. + * + * @param uri an uri string + * @param namespace set to the namespace ID + * @param identifier set to the ID in the namespace + * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a namespace URI + */ +static int +parseSubspaceURI (struct GNUNET_GE_Context *ectx, + const char *uri, + GNUNET_HashCode * namespace, char **identifier) +{ + unsigned int pos; + size_t slen; + char *up; + + GNUNET_GE_ASSERT (ectx, uri != NULL); + + slen = strlen (uri); + pos = strlen (GNUNET_ECRS_URI_PREFIX); + + if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) + return GNUNET_SYSERR; + if (0 != strncmp (&uri[pos], + GNUNET_ECRS_SUBSPACE_INFIX, + strlen (GNUNET_ECRS_SUBSPACE_INFIX))) + return GNUNET_SYSERR; + pos += strlen (GNUNET_ECRS_SUBSPACE_INFIX); + if ((slen < pos + sizeof (GNUNET_EncName) + 1) || + (!((uri[pos + sizeof (GNUNET_EncName) - 1] == '/') || + (uri[pos + sizeof (GNUNET_EncName) - 1] == '\\')))) + return GNUNET_SYSERR; + + up = GNUNET_strdup (uri); + up[pos + sizeof (GNUNET_EncName) - 1] = '\0'; + if ((GNUNET_OK != GNUNET_enc_to_hash (&up[pos], namespace))) + { + GNUNET_free (up); + return GNUNET_SYSERR; + } + *identifier = GNUNET_strdup (&up[pos + sizeof (GNUNET_EncName)]); + GNUNET_free (up); + return GNUNET_OK; +} + +/** + * Parses an URI that identifies a file + * + * @param uri an uri string + * @param fi the file identifier + * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a file URI + */ +static int +parseFileURI (struct GNUNET_GE_Context *ectx, const char *uri, + GNUNET_EC_FileIdentifier * fi) +{ + unsigned int pos; + size_t slen; + char *dup; + + GNUNET_GE_ASSERT (ectx, uri != NULL); + + slen = strlen (uri); + pos = strlen (GNUNET_ECRS_URI_PREFIX); + + if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) + return GNUNET_SYSERR; + if (0 != + strncmp (&uri[pos], GNUNET_ECRS_FILE_INFIX, + strlen (GNUNET_ECRS_FILE_INFIX))) + return GNUNET_SYSERR; + pos += strlen (GNUNET_ECRS_FILE_INFIX); + if ((slen < pos + 2 * sizeof (GNUNET_EncName) + 1) || + (uri[pos + sizeof (GNUNET_EncName) - 1] != '.') || + (uri[pos + sizeof (GNUNET_EncName) * 2 - 1] != '.')) + return GNUNET_SYSERR; + + dup = GNUNET_strdup (uri); + dup[pos + sizeof (GNUNET_EncName) - 1] = '\0'; + dup[pos + sizeof (GNUNET_EncName) * 2 - 1] = '\0'; + if ((GNUNET_OK != GNUNET_enc_to_hash (&dup[pos], + &fi->chk.key)) || + (GNUNET_OK != GNUNET_enc_to_hash (&dup[pos + sizeof (GNUNET_EncName)], + &fi->chk.query)) || + (1 != SSCANF (&dup[pos + sizeof (GNUNET_EncName) * 2], + "%llu", &fi->file_length))) + { + GNUNET_free (dup); + return GNUNET_SYSERR; + } + GNUNET_free (dup); + fi->file_length = GNUNET_htonll (fi->file_length); + return GNUNET_OK; +} + +/** + * Parses an URI that identifies a location (and file). + * Also verifies validity of the location URI. + * + * @param uri an uri string + * @param loc where to store the location + * @return GNUNET_OK on success, GNUNET_SYSERR if this is not a file URI + */ +static int +parseLocationURI (struct GNUNET_GE_Context *ectx, const char *uri, + Location * loc) +{ + unsigned int pos; + unsigned int npos; + int ret; + size_t slen; + char *dup; + char *addr; + + + GNUNET_GE_ASSERT (ectx, uri != NULL); + addr = NULL; + slen = strlen (uri); + pos = strlen (GNUNET_ECRS_URI_PREFIX); + + if (0 != strncmp (uri, GNUNET_ECRS_URI_PREFIX, pos)) + return GNUNET_SYSERR; + if (0 != strncmp (&uri[pos], + GNUNET_ECRS_LOCATION_INFIX, + strlen (GNUNET_ECRS_LOCATION_INFIX))) + return GNUNET_SYSERR; + pos += strlen (GNUNET_ECRS_LOCATION_INFIX); + if ((slen < pos + 2 * sizeof (GNUNET_EncName) + 1) || + (uri[pos + sizeof (GNUNET_EncName) - 1] != '.') || + (uri[pos + sizeof (GNUNET_EncName) * 2 - 1] != '.')) + return GNUNET_SYSERR; + + dup = GNUNET_strdup (uri); + dup[pos + sizeof (GNUNET_EncName) - 1] = '\0'; + dup[pos + sizeof (GNUNET_EncName) * 2 - 1] = '\0'; + npos = pos + sizeof (GNUNET_EncName) * 2; + while ((uri[npos] != '\0') && (uri[npos] != '.')) + npos++; + if (dup[npos] == '\0') + goto ERR; + dup[npos++] = '\0'; + if ((GNUNET_OK != GNUNET_enc_to_hash (&dup[pos], + &loc->fi.chk.key)) || + (GNUNET_OK != GNUNET_enc_to_hash (&dup[pos + sizeof (GNUNET_EncName)], + &loc->fi.chk.query)) || + (1 != SSCANF (&dup[pos + sizeof (GNUNET_EncName) * 2], + "%llu", &loc->fi.file_length))) + goto ERR; + loc->fi.file_length = GNUNET_htonll (loc->fi.file_length); + ret = enc2bin (&dup[npos], &loc->peer, sizeof (GNUNET_RSA_PublicKey)); + if (ret == -1) + goto ERR; + npos += ret; + if (dup[npos++] != '.') + goto ERR; + ret = + enc2bin (&dup[npos], &loc->contentSignature, + sizeof (GNUNET_RSA_Signature)); + if (ret == -1) + goto ERR; + npos += ret; + if (dup[npos++] != '.') + goto ERR; + if (1 != SSCANF (&dup[npos], "%u", &loc->expirationTime)) + goto ERR; + /* Finally: verify sigs! */ + if (GNUNET_OK != GNUNET_RSA_verify (&loc->fi, + sizeof (GNUNET_EC_FileIdentifier) + + sizeof (GNUNET_PeerIdentity) + + sizeof (GNUNET_Int32Time), + &loc->contentSignature, &loc->peer)) + goto ERR; + GNUNET_free (dup); + return GNUNET_OK; +ERR: + GNUNET_free (dup); + GNUNET_free_non_null (addr); + return GNUNET_SYSERR; +} + +/** + * Convert a UTF-8 String to a URI. + */ +URI * +GNUNET_ECRS_string_to_uri (struct GNUNET_GE_Context * ectx, const char *uri) +{ + URI *ret; + int len; + + ret = GNUNET_malloc (sizeof (URI)); + if (GNUNET_OK == parseFileURI (ectx, uri, &ret->data.fi)) + { + ret->type = chk; + return ret; + } + if (GNUNET_OK == parseSubspaceURI (ectx, + uri, + &ret->data.sks.namespace, + &ret->data.sks.identifier)) + { + ret->type = sks; + return ret; + } + if (GNUNET_OK == parseLocationURI (ectx, uri, &ret->data.loc)) + { + ret->type = loc; + return ret; + } + len = parseKeywordURI (ectx, uri, &ret->data.ksk.keywords); + if (len < 0) + { + GNUNET_free (ret); + return NULL; + } + ret->type = ksk; + ret->data.ksk.keywordCount = len; + return ret; +} + + + +/** + * Construct a location URI. + * + * @param baseURI content offered by the sender + * @param sender identity of the peer with the content + * @param expiration_time how long will the content be offered? + * @param proto transport protocol to reach the peer + * @param sas sender address size (for HELLO) + * @param address sas bytes of address information + * @param signer function to call for obtaining + * RSA signatures for "sender". + * @return the location URI + */ +struct GNUNET_ECRS_URI * +GNUNET_ECRS_location_to_uri (const struct GNUNET_ECRS_URI *baseUri, + const GNUNET_RSA_PublicKey * sender, + GNUNET_Int32Time expirationTime, + GNUNET_ECRS_SignFunction signer, + void *signer_cls) +{ + struct GNUNET_ECRS_URI *uri; + + if (baseUri->type != chk) + return NULL; + + uri = GNUNET_malloc (sizeof (struct GNUNET_ECRS_URI)); + uri->type = loc; + uri->data.loc.fi = baseUri->data.fi; + uri->data.loc.peer = *sender; + uri->data.loc.expirationTime = expirationTime; + signer (signer_cls, + sizeof (GNUNET_EC_FileIdentifier) + + sizeof (GNUNET_PeerIdentity) + + sizeof (GNUNET_Int32Time), + &uri->data.loc.fi, &uri->data.loc.contentSignature); + return uri; +} + +#endif + +/* end of uri.c */ diff --git a/src/fs/test_fs_getopt.c b/src/fs/test_fs_getopt.c new file mode 100644 index 000000000..2c0a0eba4 --- /dev/null +++ b/src/fs/test_fs_getopt.c @@ -0,0 +1,33 @@ +/* + 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 fs/test_fs_getopt.c + * @brief test for fs_getopt.c + * @author Christian Grothoff + */ +#include "platform.h" +#include "gnunet_fs_lib.h" + +int +main (int argc, char *argv[]) +{ + fprintf (stderr, "WARNING: testcase not yet written.\n"); + return 0; /* testcase passed */ +} diff --git a/src/fs/test_fs_uri.c b/src/fs/test_fs_uri.c new file mode 100644 index 000000000..f24f4fe27 --- /dev/null +++ b/src/fs/test_fs_uri.c @@ -0,0 +1,255 @@ +/* + This file is part of GNUnet. + (C) 2003, 2004, 2006, 2007 Christian Grothoff (and other contributing authors) + + GNUnet is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published + by the Free Software Foundation; either version 2, or (at your + option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with GNUnet; see the file COPYING. If not, write to the + Free Software Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. +*/ + +/** + * @file applications/fs/ecrs/uritest.c + * @brief Test for uri.c + * @author Christian Grothoff + */ + +#include "platform.h" +#include "gnunet_util.h" +#include "gnunet_ecrs_lib.h" +#include "ecrs.h" + +#define ABORT() { fprintf(stderr, "Error at %s:%d\n", __FILE__, __LINE__); return 1; } + +static int +testKeyword () +{ + char *uri; + struct GNUNET_ECRS_URI *ret; + + if (NULL != GNUNET_ECRS_string_to_uri (NULL, "gnunet://ecrs/ksk/++")) + ABORT (); + ret = GNUNET_ECRS_string_to_uri (NULL, "gnunet://ecrs/ksk/foo+bar"); + if (ret == NULL) + ABORT (); + if (!GNUNET_ECRS_uri_test_ksk (ret)) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + if ((2 != ret->data.ksk.keywordCount) || + (0 != strcmp (" foo", ret->data.ksk.keywords[0])) || + (0 != strcmp (" bar", ret->data.ksk.keywords[1]))) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + + uri = GNUNET_ECRS_uri_to_string (ret); + if (0 != strcmp (uri, "gnunet://ecrs/ksk/foo+bar")) + { + GNUNET_free (uri); + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + GNUNET_free (uri); + GNUNET_ECRS_uri_destroy (ret); + return 0; +} + +static int +testLocation () +{ + struct GNUNET_ECRS_URI *uri; + char *uric; + struct GNUNET_ECRS_URI *uri2; + GNUNET_RSA_PublicKey pk; + struct GNUNET_RSA_PrivateKey *hk; + struct GNUNET_ECRS_URI *baseURI; + + baseURI = + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42"); + hk = GNUNET_RSA_create_key (); + GNUNET_RSA_get_public_key (hk, &pk); + uri = GNUNET_ECRS_location_to_uri (baseURI, + &pk, 43, + (GNUNET_ECRS_SignFunction) & + GNUNET_RSA_sign, hk); + GNUNET_RSA_free_key (hk); + if (uri == NULL) + { + GNUNET_GE_BREAK (NULL, 0); + GNUNET_ECRS_uri_destroy (baseURI); + return 1; + } + if (!GNUNET_ECRS_uri_test_loc (uri)) + { + GNUNET_GE_BREAK (NULL, 0); + GNUNET_ECRS_uri_destroy (uri); + GNUNET_ECRS_uri_destroy (baseURI); + return 1; + } + uri2 = GNUNET_ECRS_uri_get_content_uri_from_loc (uri); + if (!GNUNET_ECRS_uri_test_equal (baseURI, uri2)) + { + GNUNET_GE_BREAK (NULL, 0); + GNUNET_ECRS_uri_destroy (uri); + GNUNET_ECRS_uri_destroy (uri2); + GNUNET_ECRS_uri_destroy (baseURI); + return 1; + } + GNUNET_ECRS_uri_destroy (uri2); + GNUNET_ECRS_uri_destroy (baseURI); + uric = GNUNET_ECRS_uri_to_string (uri); +#if 0 + /* not for the faint of heart: */ + printf ("URI: `%s'\n", uric); +#endif + uri2 = GNUNET_ECRS_string_to_uri (NULL, uric); + GNUNET_free (uric); + if (uri2 == NULL) + { + GNUNET_GE_BREAK (NULL, 0); + GNUNET_ECRS_uri_destroy (uri); + return 1; + } + if (GNUNET_YES != GNUNET_ECRS_uri_test_equal (uri, uri2)) + { + GNUNET_GE_BREAK (NULL, 0); + GNUNET_ECRS_uri_destroy (uri); + GNUNET_ECRS_uri_destroy (uri2); + return 1; + } + GNUNET_ECRS_uri_destroy (uri2); + GNUNET_ECRS_uri_destroy (uri); + return 0; +} + +static int +testNamespace (int i) +{ + char *uri; + struct GNUNET_ECRS_URI *ret; + + if (NULL != + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/sks/D1KJS9H2A82Q65VKQ0ML3RFU6U1D3VUK")) + ABORT (); + if (NULL != + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/sks/D1KJS9H2A82Q65VKQ0ML3RFU6U1D3V/test")) + ABORT (); + if (NULL != GNUNET_ECRS_string_to_uri (NULL, "gnunet://ecrs/sks/test")) + ABORT (); + ret = + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/sks/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820/test"); + if (ret == NULL) + ABORT (); + if (GNUNET_ECRS_uri_test_ksk (ret)) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + if (!GNUNET_ECRS_uri_test_sks (ret)) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + + uri = GNUNET_ECRS_uri_to_string (ret); + if (0 != strcmp (uri, + "gnunet://ecrs/sks/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820/test")) + { + GNUNET_ECRS_uri_destroy (ret); + GNUNET_free (uri); + ABORT (); + } + GNUNET_free (uri); + GNUNET_ECRS_uri_destroy (ret); + return 0; +} + +static int +testFile (int i) +{ + char *uri; + struct GNUNET_ECRS_URI *ret; + + if (NULL != + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H00000440000.42")) + ABORT (); + if (NULL != + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000")) + ABORT (); + if (NULL != + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.FGH")) + ABORT (); + ret = + GNUNET_ECRS_string_to_uri (NULL, + "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42"); + if (ret == NULL) + ABORT (); + if (GNUNET_ECRS_uri_test_ksk (ret)) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + if (GNUNET_ECRS_uri_test_sks (ret)) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + if (GNUNET_ntohll (ret->data.fi.file_length) != 42) + { + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + + uri = GNUNET_ECRS_uri_to_string (ret); + if (0 != strcmp (uri, + "gnunet://ecrs/chk/C282GG70GKK41O4551011DO413KFBVTVMQG1OG30I0K4045N0G41HAPB82G680A02JRVVFO8URVRU2F159011DO41000000022RG820.RNVVVVOOLCLK065B5D04HTNVNSIB2AI022RG8200HSLK1CO1000ATQ98824DMA2032LIMG50CG0K057NVUVG200000H000004400000.42")) + { + GNUNET_free (uri); + GNUNET_ECRS_uri_destroy (ret); + ABORT (); + } + GNUNET_free (uri); + GNUNET_ECRS_uri_destroy (ret); + return 0; +} + +int +main (int argc, char *argv[]) +{ + int failureCount = 0; + int i; + + GNUNET_disable_entropy_gathering (); + failureCount += testKeyword (); + failureCount += testLocation (); + for (i = 0; i < 255; i++) + { + failureCount += testNamespace (i); + failureCount += testFile (i); + } + if (failureCount != 0) + return 1; + return 0; +} + +/* end of uritest.c */ diff --git a/src/hostlist/hostlist-client.c b/src/hostlist/hostlist-client.c index bc98a2bcf..468c6b64c 100644 --- a/src/hostlist/hostlist-client.c +++ b/src/hostlist/hostlist-client.c @@ -625,11 +625,12 @@ GNUNET_HOSTLIST_client_start (const struct GNUNET_CONFIGURATION_Handle *c, cfg = c; sched = s; stats = st; - proxy = NULL; - GNUNET_CONFIGURATION_get_value_string (cfg, - "HOSTLIST", - "HTTP-PROXY", - &proxy); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "HOSTLIST", + "HTTP-PROXY", + &proxy)) + proxy = NULL; *ch = &connect_handler; *dh = &disconnect_handler; GNUNET_STATISTICS_get (stats, diff --git a/src/hostlist/hostlist-server.c b/src/hostlist/hostlist-server.c index 34e9310e4..5cc26c409 100644 --- a/src/hostlist/hostlist-server.c +++ b/src/hostlist/hostlist-server.c @@ -116,7 +116,10 @@ host_processor (void *cls, size_t s; if (peer == NULL) - finish_response (results); + { + finish_response (results); + return; + } old = results->size; s = GNUNET_HELLO_size(hello); if (old + s >= GNUNET_MAX_MALLOC_CHECKED) diff --git a/src/include/gnunet_configuration_lib.h b/src/include/gnunet_configuration_lib.h index abbfe2eec..c5ea21ed8 100644 --- a/src/include/gnunet_configuration_lib.h +++ b/src/include/gnunet_configuration_lib.h @@ -47,16 +47,27 @@ struct GNUNET_CONFIGURATION_Handle; /** * Create a new configuration object. - * - * @param component name of responsible component + * @return fresh configuration object */ struct GNUNET_CONFIGURATION_Handle *GNUNET_CONFIGURATION_create (void); + +/** + * Duplicate an existing configuration object. + * + * @param c configuration to duplicate + * @return duplicate configuration + */ +struct GNUNET_CONFIGURATION_Handle * +GNUNET_CONFIGURATION_dup (const struct GNUNET_CONFIGURATION_Handle *c); + + /** * Destroy configuration object. */ void GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg); + /** * Load configuration. This function will first parse the * defaults and then parse the specific configuration file @@ -68,6 +79,7 @@ void GNUNET_CONFIGURATION_destroy (struct GNUNET_CONFIGURATION_Handle *cfg); int GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg, const char *filename); + /** * Parse a configuration file, add all of the options in the * file to the configuration environment. @@ -76,6 +88,7 @@ int GNUNET_CONFIGURATION_load (struct GNUNET_CONFIGURATION_Handle *cfg, int GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg, const char *filename); + /** * Write configuration file. * @return GNUNET_OK on success, GNUNET_SYSERR on error @@ -83,6 +96,7 @@ int GNUNET_CONFIGURATION_parse (struct GNUNET_CONFIGURATION_Handle *cfg, int GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg, const char *filename); + /** * Test if there are configuration options that were * changed since the last save. @@ -90,6 +104,33 @@ int GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *cfg, */ int GNUNET_CONFIGURATION_is_dirty (const struct GNUNET_CONFIGURATION_Handle *cfg); + +/** + * Function to iterate over options. + * + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option + */ +typedef void (*GNUNET_CONFIGURATION_Iterator)(void *cls, + const char *section, + const char *option, + const char *value); + + +/** + * 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); + + /** * Get a configuration value that should be a number. * @return GNUNET_OK on success, GNUNET_SYSERR on error @@ -240,6 +281,7 @@ int GNUNET_CONFIGURATION_append_value_filename (struct const char *option, const char *value); + #if 0 /* keep Emacsens' auto-indent happy */ { #endif diff --git a/src/include/gnunet_container_lib.h b/src/include/gnunet_container_lib.h index 255f68a89..c3a9a79ba 100644 --- a/src/include/gnunet_container_lib.h +++ b/src/include/gnunet_container_lib.h @@ -189,11 +189,15 @@ struct GNUNET_CONTAINER_MetaData; /** * Iterator over meta data. + * + * @param cls closure + * @param type type of the meta data + * @param data value of the meta data * @return GNUNET_OK to continue to iterate, GNUNET_SYSERR to abort */ -typedef int (*GNUNET_CONTAINER_MetaDataProcessor) (EXTRACTOR_KeywordType type, - const char *data, - void *closure); +typedef int (*GNUNET_CONTAINER_MetaDataProcessor) (void *cls, + EXTRACTOR_KeywordType type, + const char *data); /** * Create a fresh MetaData token. diff --git a/src/include/gnunet_testing_lib.h b/src/include/gnunet_testing_lib.h index dbd8a3d3f..8de354853 100644 --- a/src/include/gnunet_testing_lib.h +++ b/src/include/gnunet_testing_lib.h @@ -85,7 +85,7 @@ typedef void (*GNUNET_TESTING_NotifyDaemonRunning)(void *cls, */ struct GNUNET_TESTING_Daemon * GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CONFIGURATION_Handle *cfg, const char *hostname, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls); @@ -163,50 +163,17 @@ struct GNUNET_TESTING_PeerGroup; * @param total number of daemons to start * @param cb function to call on each daemon that was started * @param cb_cls closure for cb - * @param hostname where to run the peers; can be NULL (to run - * everything on localhost). - * @param va Additional hosts can be specified using a NULL-terminated list of - * varargs, hosts will then be used round-robin from that - * list; va only contains anything if hostname != NULL. - * @return NULL on error, otherwise handle to control peer group - */ -struct GNUNET_TESTING_PeerGroup * -GNUNET_TESTING_daemons_start_va (struct GNUNET_SCHEDULER_Handle *sched, - const struct GNUNET_CONFIGURATION_Handle *cfg, - unsigned int total, - GNUNET_TESTING_NotifyDaemonRunning cb, - void *cb_cls, - const char *hostname, - va_list va); - - -/** - * Start count gnunetd processes with the same set of - * transports and applications. The port numbers will - * be computed by adding delta each time (zero - * times for the first peer). - * - * @param sched scheduler to use - * @param cfg configuration template to use - * @param total number of daemons to start - * @param timeout how long is this allowed to take? - * @param cb function to call on each daemon that was started - * @param cb_cls closure for cb - * @param hostname where to run the peers; can be NULL (to run - * everything on localhost). Additional - * hosts can be specified using a NULL-terminated list of - * varargs, hosts will then be used round-robin from that - * list. + * @param hostnames space-separated list of hostnames to use, + * NULL to use localhost only * @return NULL on error, otherwise handle to control peer group */ struct GNUNET_TESTING_PeerGroup * GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned int total, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls, - const char *hostname, - ...); + const char *hostnames); /** diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am index 635570482..9ac831a88 100644 --- a/src/testing/Makefile.am +++ b/src/testing/Makefile.am @@ -15,17 +15,20 @@ libgnunettesting_la_SOURCES = \ testing.c \ testing_group.c \ testing_testbed.c -libgnunettesting_la_LIBADD = \ - $(top_builddir)/src/util/libgnunetutil.la $(XLIB) - -#check_PROGRAMS = \ -# test_testing -# -#TESTS = $(check_PROGRAMS) -# -#test_testing_SOURCES = \ -# test_testing.c -#test_testing_LDADD = \ -# $(top_builddir)/src/testing/libgnunettesting.la \ -# $(top_builddir)/src/util/libgnunetutil.la +libgnunettesting_la_LIBADD = $(XLIB) \ + $(top_builddir)/src/core/libgnunetcore.la \ + $(top_builddir)/src/transport/libgnunettransport.la \ + $(top_builddir)/src/util/libgnunetutil.la +check_PROGRAMS = \ + test_testing + +TESTS = $(check_PROGRAMS) + +test_testing_SOURCES = \ + test_testing.c +test_testing_LDADD = \ + $(top_builddir)/src/testing/libgnunettesting.la \ + $(top_builddir)/src/util/libgnunetutil.la + +EXTRA_DIST = test_testing_data.conf diff --git a/src/testing/test_testing.c b/src/testing/test_testing.c new file mode 100644 index 000000000..cbedf60fb --- /dev/null +++ b/src/testing/test_testing.c @@ -0,0 +1,116 @@ +/* + 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 testing/test_testing.c + * @brief testcase for testing.c + */ +#include "platform.h" +#include "gnunet_testing_lib.h" + +#define VERBOSE GNUNET_YES + +static int ok; + +static void end_cb(void *cls, + const char *emsg) +{ + GNUNET_assert (emsg == NULL); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Daemon terminated, will now exit.\n"); +#endif + ok = 0; +} + +static void my_cb(void *cls, + const struct GNUNET_PeerIdentity *id, + const struct GNUNET_CONFIGURATION_Handle *cfg, + struct GNUNET_TESTING_Daemon *d, + const char *emsg) +{ + GNUNET_assert (id != NULL); +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Daemon started, will now stop it.\n"); +#endif + GNUNET_TESTING_daemon_stop (d, &end_cb, NULL); +} + + +static void +run (void *cls, + struct GNUNET_SCHEDULER_Handle *sched, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + struct GNUNET_TESTING_Daemon *d; + + ok = 1; +#if VERBOSE + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Starting daemon.\n"); +#endif + d = GNUNET_TESTING_daemon_start (sched, + cfg, + NULL, + &my_cb, + NULL); + GNUNET_assert (d != NULL); +} + +static int +check () +{ + char *const argv[] = { "test-testing", + "-c", + "test_testing_data.conf", +#if VERBOSE + "-L", "DEBUG", +#endif + NULL + }; + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_OPTION_END + }; + GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1, + argv, "test-tesing", "nohelp", + options, &run, &ok); + return ok; +} + +int +main (int argc, char *argv[]) +{ + int ret; + + GNUNET_log_setup ("test-testing", +#if VERBOSE + "DEBUG", +#else + "WARNING", +#endif + NULL); + ret = check (); + + return ret; +} + +/* end of test_testing.c */ diff --git a/src/testing/test_testing_data.conf b/src/testing/test_testing_data.conf new file mode 100644 index 000000000..7c46fdf34 --- /dev/null +++ b/src/testing/test_testing_data.conf @@ -0,0 +1,29 @@ +[PATHS] +SERVICEHOME = /tmp/test-gnunet-testing/ +DEFAULTCONFIG = test_testing_data.conf + +[resolver] +PORT = 2564 + +[transport] +PORT = 2565 +PLUGINS = tcp + +[arm] +PORT = 2566 +DEFAULTSERVICES = transport core + +[statistics] +PORT = 2567 + +[tcp] +PORT = 2568 + +[peerinfo] +PORT = 2569 + +[core] +PORT = 2570 + +[testing] +WEAKRANDOM = YES diff --git a/src/testing/testing.c b/src/testing/testing.c index 3406355c5..5d465e05f 100644 --- a/src/testing/testing.c +++ b/src/testing/testing.c @@ -85,7 +85,7 @@ struct GNUNET_TESTING_Daemon /** * Our configuration. */ - const struct GNUNET_CONFIGURATION_Handle *cfg; + struct GNUNET_CONFIGURATION_Handle *cfg; /** * Host to run GNUnet on. @@ -202,10 +202,11 @@ testing_init (void *cls, d->cb = NULL; if (server == NULL) { - cb (d->cb_cls, NULL, d->cfg, d, - _("Failed to connect to core service\n")); if (GNUNET_YES == d->dead) GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); + else if (NULL != cb) + cb (d->cb_cls, NULL, d->cfg, d, + _("Failed to connect to core service\n")); return; } #if DEBUG_TESTING @@ -216,7 +217,7 @@ testing_init (void *cls, d->id = *my_identity; if (GNUNET_YES == d->dead) GNUNET_TESTING_daemon_stop (d, d->dead_cb, d->dead_cb_cls); - else + else if (NULL != cb) cb (d->cb_cls, my_identity, d->cfg, d, NULL); d->server = server; } @@ -240,6 +241,11 @@ start_fsm (void *cls, unsigned long code; char *dst; +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer FSM is in phase %u.\n", + d->phase); +#endif d->task = GNUNET_SCHEDULER_NO_TASK; switch (d->phase) { @@ -255,11 +261,12 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - _("`scp' does not seem to terminate.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`scp' does not seem to terminate.\n")); return; } /* wait some more */ @@ -278,11 +285,12 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - _("`scp' did not complete cleanly.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`scp' did not complete cleanly.\n")); return; } #if DEBUG_TESTING @@ -299,7 +307,11 @@ start_fsm (void *cls, "gnunet-service-arm", "-c", d->cfgfile, +#if DEBUG_TESTING + "-L", "DEBUG", +#else "-d", +#endif NULL); } else @@ -328,16 +340,31 @@ start_fsm (void *cls, (NULL == d->hostname) ? "gnunet-service-arm" : "ssh"); cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - (NULL == d->hostname) - ? _("Failed to start `gnunet-service-arm' process.\n") - : _("Failed to start `ssh' process.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("Failed to start `gnunet-service-arm' process.\n") + : _("Failed to start `ssh' process.\n")); } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Started `%s', waiting for `%s' to be up.\n", + "gnunet-service-arm", + "gnunet-service-core"); +#endif d->phase = SP_START_ARMING; d->wait_runs = 0; + d->task + = GNUNET_SCHEDULER_add_delayed (d->sched, + GNUNET_NO, + GNUNET_SCHEDULER_PRIORITY_KEEP, + GNUNET_SCHEDULER_NO_TASK, + GNUNET_CONSTANTS_EXEC_WAIT, + &start_fsm, + d); break; case SP_START_ARMING: if (GNUNET_OK != @@ -350,13 +377,14 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - (NULL == d->hostname) - ? _("`gnunet-service-arm' does not seem to terminate.\n") - : _("`ssh' does not seem to terminate.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + (NULL == d->hostname) + ? _("`gnunet-service-arm' does not seem to terminate.\n") + : _("`ssh' does not seem to terminate.\n")); return; } /* wait some more */ @@ -424,18 +452,24 @@ start_fsm (void *cls, if ( (type != GNUNET_OS_PROCESS_EXITED) || (code != 0) ) { - d->dead_cb (d->dead_cb_cls, - _("`sshp' did not complete cleanly.\n")); + if (NULL != d->dead_cb) + d->dead_cb (d->dead_cb_cls, + _("`ssh' did not complete cleanly.\n")); GNUNET_free (d->cfgfile); GNUNET_free_non_null (d->hostname); GNUNET_free_non_null (d->username); GNUNET_free (d); return; } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Peer shutdown complete.\n"); +#endif GNUNET_free (d->cfgfile); GNUNET_free_non_null (d->hostname); GNUNET_free_non_null (d->username); - d->dead_cb (d->dead_cb_cls, NULL); + if (NULL != d->dead_cb) + d->dead_cb (d->dead_cb_cls, NULL); GNUNET_free (d); break; case SP_CONFIG_UPDATE: @@ -450,11 +484,12 @@ start_fsm (void *cls, { cb = d->cb; d->cb = NULL; - cb (d->cb_cls, - NULL, - d->cfg, - d, - _("`scp' does not seem to terminate.\n")); + if (NULL != cb) + cb (d->cb_cls, + NULL, + d->cfg, + d, + _("`scp' does not seem to terminate.\n")); return; } /* wait some more */ @@ -471,15 +506,17 @@ start_fsm (void *cls, if ( (type != GNUNET_OS_PROCESS_EXITED) || (code != 0) ) { - d->update_cb (d->update_cb_cls, - _("`scp' did not complete cleanly.\n")); + if (NULL != d->update_cb) + d->update_cb (d->update_cb_cls, + _("`scp' did not complete cleanly.\n")); return; } #if DEBUG_TESTING GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Successfully copied configuration file.\n"); #endif - d->update_cb (d->update_cb_cls, NULL); + if (NULL != d->update_cb) + d->update_cb (d->update_cb_cls, NULL); d->phase = SP_START_DONE; break; } @@ -502,7 +539,7 @@ start_fsm (void *cls, */ struct GNUNET_TESTING_Daemon * GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CONFIGURATION_Handle *cfg, const char *hostname, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls) @@ -513,9 +550,13 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, ret = GNUNET_malloc (sizeof(struct GNUNET_TESTING_Daemon)); ret->sched = sched; - ret->cfg = cfg; ret->hostname = (hostname == NULL) ? NULL : GNUNET_strdup (hostname); ret->cfgfile = GNUNET_DISK_mktemp ("gnunet-testing-config"); +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Setting up peer with configuration file `%s'.\n", + ret->cfgfile); +#endif if (NULL == ret->cfgfile) { GNUNET_free_non_null (ret->hostname); @@ -524,15 +565,21 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, } ret->cb = cb; ret->cb_cls = cb_cls; + ret->cfg = GNUNET_CONFIGURATION_dup (cfg); + GNUNET_CONFIGURATION_set_value_string (ret->cfg, + "PATHS", + "DEFAULTCONFIG", + ret->cfgfile); /* 1) write configuration to temporary file */ if (GNUNET_OK != - GNUNET_CONFIGURATION_write (cfg, + GNUNET_CONFIGURATION_write (ret->cfg, ret->cfgfile)) { if (0 != UNLINK (ret->cfgfile)) GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", ret->cfgfile); + GNUNET_CONFIGURATION_destroy (ret->cfg); GNUNET_free_non_null (ret->hostname); GNUNET_free (ret->cfgfile); GNUNET_free (ret); @@ -581,6 +628,7 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "unlink", ret->cfgfile); + GNUNET_CONFIGURATION_destroy (ret->cfg); GNUNET_free_non_null (ret->hostname); GNUNET_free_non_null (ret->username); GNUNET_free (ret->cfgfile); @@ -589,7 +637,7 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, } ret->task = GNUNET_SCHEDULER_add_delayed (sched, - GNUNET_NO, + GNUNET_YES, GNUNET_SCHEDULER_PRIORITY_KEEP, GNUNET_SCHEDULER_NO_TASK, GNUNET_CONSTANTS_EXEC_WAIT, @@ -597,6 +645,10 @@ GNUNET_TESTING_daemon_start (struct GNUNET_SCHEDULER_Handle *sched, ret); return ret; } +#if DEBUG_TESTING + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "No need to copy configuration file since we are running locally.\n"); +#endif ret->phase = SP_COPIED; GNUNET_SCHEDULER_add_continuation (sched, GNUNET_NO, @@ -687,7 +739,7 @@ void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, d->dead_cb_cls = cb_cls; d->task = GNUNET_SCHEDULER_add_delayed (d->sched, - GNUNET_NO, + GNUNET_YES, GNUNET_SCHEDULER_PRIORITY_KEEP, GNUNET_SCHEDULER_NO_TASK, GNUNET_CONSTANTS_EXEC_WAIT, @@ -695,11 +747,13 @@ void GNUNET_TESTING_daemon_stop (struct GNUNET_TESTING_Daemon *d, d); return; } + GNUNET_CONFIGURATION_destroy (d->cfg); GNUNET_free (d->cfgfile); GNUNET_free_non_null (d->hostname); GNUNET_free_non_null (d->username); GNUNET_free (d); - cb (cb_cls, NULL); + if (NULL != cb) + cb (cb_cls, NULL); } @@ -720,8 +774,9 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, if (d->phase != SP_START_DONE) { - cb (cb_cls, - _("Peer not yet running, can not change configuration at this point.")); + if (NULL != cb) + cb (cb_cls, + _("Peer not yet running, can not change configuration at this point.")); return; } @@ -730,7 +785,8 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, GNUNET_CONFIGURATION_write (cfg, d->cfgfile)) { - cb (cb_cls, + if (NULL != cb) + cb (cb_cls, _("Failed to write new configuration to disk.")); return; } @@ -739,7 +795,8 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, if (NULL == d->hostname) { /* signal success */ - cb (cb_cls, NULL); + if (NULL != cb) + cb (cb_cls, NULL); return; } d->phase = SP_CONFIG_UPDATE; @@ -765,8 +822,9 @@ void GNUNET_TESTING_daemon_reconfigure (struct GNUNET_TESTING_Daemon *d, GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Could not start `%s' process to copy configuration file.\n"), "scp"); - cb (cb_cls, - _("Failed to copy new configuration to remote machine.")); + if (NULL != cb) + cb (cb_cls, + _("Failed to copy new configuration to remote machine.")); d->phase = SP_START_DONE; return; } @@ -802,10 +860,14 @@ static size_t transmit_ready (void *cls, size_t size, void *buf) { struct ConnectContext *ctx = cls; - if (buf == NULL) - ctx->cb (ctx->cb_cls, _("Peers failed to connect")); - else - ctx->cb (ctx->cb_cls, NULL); + + if (NULL != ctx->cb) + { + if (buf == NULL) + ctx->cb (ctx->cb_cls, _("Peers failed to connect")); + else + ctx->cb (ctx->cb_cls, NULL); + } GNUNET_free (ctx); return 0; } @@ -831,8 +893,9 @@ process_hello (void *cls, if (peer == NULL) { /* signal error */ - ctx->cb (ctx->cb_cls, - _("Failed to receive `HELLO' from peer\n")); + if (NULL != ctx->cb) + ctx->cb (ctx->cb_cls, + _("Failed to receive `HELLO' from peer\n")); GNUNET_TRANSPORT_disconnect (ctx->d1th); GNUNET_TRANSPORT_disconnect (ctx->d2th); GNUNET_free (ctx); @@ -873,7 +936,8 @@ void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, if ( (d1->server == NULL) || (d2->server == NULL) ) { - cb (cb_cls, _("Peers are not fully running yet, can not connect!\n")); + if (NULL != cb) + cb (cb_cls, _("Peers are not fully running yet, can not connect!\n")); return; } ctx = GNUNET_malloc (sizeof(struct ConnectContext)); @@ -886,7 +950,8 @@ void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, if (ctx->d1th == NULL) { GNUNET_free (ctx); - cb (cb_cls, _("Failed to connect to transport service!\n")); + if (NULL != cb) + cb (cb_cls, _("Failed to connect to transport service!\n")); return; } ctx->d2th = GNUNET_TRANSPORT_connect (d2->sched, d2->cfg, d2, NULL, NULL, NULL); @@ -894,7 +959,8 @@ void GNUNET_TESTING_daemons_connect (struct GNUNET_TESTING_Daemon *d1, { GNUNET_TRANSPORT_disconnect (ctx->d1th); GNUNET_free (ctx); - cb (cb_cls, _("Failed to connect to transport service!\n")); + if (NULL != cb) + cb (cb_cls, _("Failed to connect to transport service!\n")); return; } GNUNET_TRANSPORT_get_hello (ctx->d1th, diff --git a/src/testing/testing_group.c b/src/testing/testing_group.c index 917a524de..b87507364 100644 --- a/src/testing/testing_group.c +++ b/src/testing/testing_group.c @@ -27,6 +27,58 @@ #include "gnunet_arm_service.h" #include "gnunet_testing_lib.h" +/** + * Lowest port used for GNUnet testing. Should be high enough to not + * conflict with other applications running on the hosts but be low + * enough to not conflict with client-ports (typically starting around + * 32k). + */ +#define LOW_PORT 10000 + +/** + * Highest port used for GNUnet testing. Should be low enough to not + * conflict with the port range for "local" ports (client apps; see + * /proc/sys/net/ipv4/ip_local_port_range on Linux for example). + */ +#define HIGH_PORT 32000 + +/** + * Data we keep per peer. + */ +struct PeerData +{ + /** + * (Initial) configuration of the host. + * (initial because clients could change + * it and we would not know about those + * updates). + */ + struct GNUNET_CONFIGURATION_Handle *cfg; + + /** + * Handle for controlling the daemon. + */ + struct GNUNET_TESTING_Daemon *daemon; +}; + + +/** + * Data we keep per host. + */ +struct HostData +{ + /** + * Name of the host. + */ + char *hostname; + + /** + * Lowest port that we have not yet used + * for GNUnet. + */ + uint16_t minport; +}; + /** * Handle to a group of GNUnet peers. @@ -41,7 +93,7 @@ struct GNUNET_TESTING_PeerGroup /** * Configuration template. */ - struct GNUNET_CONFIGURATION_Handle *cfg; + const struct GNUNET_CONFIGURATION_Handle *cfg; /** * Function to call on each started daemon. @@ -54,14 +106,15 @@ struct GNUNET_TESTING_PeerGroup void *cb_cls; /** - * NULL-terminated array of hostnames. + * NULL-terminated array of information about + * hosts. */ - char **hostnames; + struct HostData *hosts; /** * Array of "total" peers. */ - struct GNUNET_TESTING_Daemon **peers; + struct PeerData *peers; /** * Number of peers in this group. @@ -71,79 +124,212 @@ struct GNUNET_TESTING_PeerGroup }; +struct UpdateContext +{ + struct GNUNET_CONFIGURATION_Handle *ret; + unsigned int nport; +}; + /** - * Start count gnunetd processes with the same set of transports and - * applications. The port numbers (any option called "PORT") will be - * adjusted to ensure that no two peers running on the same system - * have the same port(s) in their respective configurations. + * Function to iterate over options. Copies + * the options to the target configuration, + * updating PORT values as needed. * - * @param sched scheduler to use - * @param cfg configuration template to use - * @param total number of daemons to start - * @param cb function to call on each daemon that was started - * @param cb_cls closure for cb - * @param hostname where to run the peers; can be NULL (to run - * everything on localhost). - * @param va Additional hosts can be specified using a NULL-terminated list of - * varargs, hosts will then be used round-robin from that - * list; va only contains anything if hostname != NULL. - * @return NULL on error, otherwise handle to control peer group + * @param cls closure + * @param section name of the section + * @param option name of the option + * @param value value of the option */ -struct GNUNET_TESTING_PeerGroup * -GNUNET_TESTING_daemons_start_va (struct GNUNET_SCHEDULER_Handle *sched, - const struct GNUNET_CONFIGURATION_Handle *cfg, - unsigned int total, - GNUNET_TESTING_NotifyDaemonRunning cb, - void *cb_cls, - const char *hostname, - va_list va) +static void +update_config(void *cls, + const char *section, + const char *option, + const char *value) { - struct GNUNET_TESTING_PeerGroup *pg; - - pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup)); - return pg; + struct UpdateContext *ctx = cls; + unsigned int ival; + char cval[12]; + + if ( (0 == strcmp (option, "PORT")) && + (1 == sscanf (value, "%u", &ival)) ) + { + GNUNET_snprintf (cval, + sizeof(cval), + "%u", + ctx->nport++); + value = cval; + } + GNUNET_CONFIGURATION_set_value_string (ctx->ret, + section, + option, + value); } /** - * Start count gnunetd processes with the same set of - * transports and applications. The port numbers will - * be computed by adding delta each time (zero - * times for the first peer). + * Create a new configuration using the given configuration + * as a template; however, each PORT in the existing cfg + * must be renumbered by incrementing "*port". If we run + * out of "*port" numbers, return NULL. + * + * @param cfg template configuration + * @param port port numbers to use, update to reflect + * port numbers that were used + * @return new configuration, NULL on error + */ +static struct GNUNET_CONFIGURATION_Handle* +make_config (const struct GNUNET_CONFIGURATION_Handle*cfg, + uint16_t *port) +{ + struct UpdateContext uc; + uint16_t orig; + + orig = *port; + uc.nport = *port; + uc.ret = GNUNET_CONFIGURATION_create (); + GNUNET_CONFIGURATION_iterate (cfg, + &update_config, + &uc); + if (uc.nport >= HIGH_PORT) + { + *port = orig; + GNUNET_CONFIGURATION_destroy (uc.ret); + return NULL; + } + *port = (uint16_t) uc.nport; + return uc.ret; +} + + +/** + * Start count gnunetd processes with the same set of transports and + * applications. The port numbers (any option called "PORT") will be + * adjusted to ensure that no two peers running on the same system + * have the same port(s) in their respective configurations. * * @param sched scheduler to use * @param cfg configuration template to use * @param total number of daemons to start - * @param timeout how long is this allowed to take? * @param cb function to call on each daemon that was started * @param cb_cls closure for cb - * @param hostname where to run the peers; can be NULL (to run - * everything on localhost). Additional - * hosts can be specified using a NULL-terminated list of - * varargs, hosts will then be used round-robin from that - * list. + * @param hostnames space-separated list of hostnames to use; can be NULL (to run + * everything on localhost). * @return NULL on error, otherwise handle to control peer group */ struct GNUNET_TESTING_PeerGroup * GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, - struct GNUNET_CONFIGURATION_Handle *cfg, + const struct GNUNET_CONFIGURATION_Handle *cfg, unsigned int total, GNUNET_TESTING_NotifyDaemonRunning cb, void *cb_cls, - const char *hostname, - ...) + const char *hostnames) { - struct GNUNET_TESTING_PeerGroup * ret; - va_list va; - - va_start (va, hostname); - ret = GNUNET_TESTING_daemons_start_va (sched, cfg, - total, cb, cb_cls, hostname, - va); - va_end (va); - return ret; -} + struct GNUNET_TESTING_PeerGroup *pg; + const char *rpos; + char *pos; + char *start; + const char *hostname; + struct GNUNET_CONFIGURATION_Handle *pcfg; + unsigned int off; + unsigned int hostcnt; + uint16_t minport; + if (0 == total) + { + GNUNET_break (0); + return NULL; + } + pg = GNUNET_malloc (sizeof(struct GNUNET_TESTING_PeerGroup)); + pg->sched = sched; + pg->cfg = cfg; + pg->cb = cb; + pg->cb_cls = cb_cls; + pg->total = total; + pg->peers = GNUNET_malloc (total * sizeof(struct PeerData)); + if (NULL != hostnames) + { + off = 2; + /* skip leading spaces */ + while ( (0 != *hostnames) && + (isspace(*hostnames))) + hostnames++; + rpos = hostnames; + while ('\0' != *rpos) + { + if (isspace (*rpos)) + off++; + rpos++; + } + pg->hosts = GNUNET_malloc (off * sizeof (struct HostData)); + off = 0; + start = GNUNET_strdup (hostnames); + pos = start; + while ('\0' != *pos) + { + if (isspace (*pos)) + { + *pos = '\0'; + if (strlen(start) > 0) + { + pg->hosts[off].minport = LOW_PORT; + pg->hosts[off++].hostname = start; + } + start = pos+1; + } + pos++; + } + if (strlen(start) > 0) + { + pg->hosts[off].minport = LOW_PORT; + pg->hosts[off++].hostname = start; + } + if (off == 0) + { + GNUNET_free (start); + GNUNET_free (pg->hosts); + pg->hosts = NULL; + } + hostcnt = off; + minport = 0; /* make gcc happy */ + } + else + { + hostcnt = 0; + minport = LOW_PORT; + } + for (off = 0; off < total; off++) + { + if (hostcnt > 0) + { + hostname = pg->hosts[off % hostcnt].hostname; + pcfg = make_config (cfg, &pg->hosts[off % hostcnt].minport); + } + else + { + hostname = NULL; + pcfg = make_config (cfg, &minport); + } + if (NULL == pcfg) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not create configuration for peer number %u on `%s'!\n"), + off, + hostname == NULL ? "localhost" : hostname); + continue; + } + pg->peers[off].cfg = pcfg; + pg->peers[off].daemon = GNUNET_TESTING_daemon_start (sched, + pcfg, + hostname, + cb, + cb_cls); + if (NULL == pg->peers[off].daemon) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Could not start peer number %u!\n"), + off); + } + return pg; +} /** @@ -154,7 +340,26 @@ GNUNET_TESTING_daemons_start (struct GNUNET_SCHEDULER_Handle *sched, void GNUNET_TESTING_daemons_stop (struct GNUNET_TESTING_PeerGroup *pg) { - + unsigned int off; + + for (off = 0; off < pg->total; off++) + { + /* FIXME: should we wait for our + continuations to be called here? This + would require us to take a continuation + as well... */ + if (NULL != pg->peers[off].daemon) + GNUNET_TESTING_daemon_stop (pg->peers[off].daemon, + NULL, NULL); + if (NULL != pg->peers[off].cfg) + GNUNET_CONFIGURATION_destroy (pg->peers[off].cfg); + } + GNUNET_free (pg->peers); + if (NULL != pg->hosts) + { + GNUNET_free (pg->hosts[0].hostname); + GNUNET_free (pg->hosts); + } GNUNET_free (pg); } diff --git a/src/topology/gnunet-daemon-topology.c b/src/topology/gnunet-daemon-topology.c index a67023542..12171139e 100644 --- a/src/topology/gnunet-daemon-topology.c +++ b/src/topology/gnunet-daemon-topology.c @@ -508,7 +508,7 @@ consider_for_advertising (const struct GNUNET_HELLO_Message *hello) &have_address); if (GNUNET_NO == have_address) return; /* no point in advertising this one... */ - GNUNET_HELLO_get_id (hello, &pid); + GNUNET_break (GNUNET_OK == GNUNET_HELLO_get_id (hello, &pid)); pos = hellos; while (pos != NULL) { @@ -736,11 +736,18 @@ read_friends_file (const struct GNUNET_CONFIGURATION_Handle *cfg) unsigned int entries_found; struct PeerList *fl; - fn = NULL; - GNUNET_CONFIGURATION_get_value_filename (cfg, - "TOPOLOGY", - "FRIENDS", - &fn); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_filename (cfg, + "TOPOLOGY", + "FRIENDS", + &fn)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _("Option `%s' in section `%s' not specified!\n"), + "FRIENDS", + "TOPOLOGY"); + return; + } if (GNUNET_OK != GNUNET_DISK_file_test (fn)) GNUNET_DISK_fn_write (fn, NULL, 0, GNUNET_DISK_PERM_USER_READ | GNUNET_DISK_PERM_USER_WRITE); @@ -920,7 +927,7 @@ hello_advertising (void *cls, if (0 == GNUNET_TIME_absolute_get_remaining (pos->expiration).value) { /* time to discard... */ - if (prev == NULL) + if (prev != NULL) prev->next = next; else hellos = next; @@ -1015,17 +1022,19 @@ run (void *cls, friends_only = GNUNET_CONFIGURATION_get_value_yesno (cfg, "TOPOLOGY", "FRIENDS-ONLY"); - opt = 0; - GNUNET_CONFIGURATION_get_value_number (cfg, - "TOPOLOGY", - "MINIMUM-FRIENDS", - &opt); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "TOPOLOGY", + "MINIMUM-FRIENDS", + &opt)) + opt = 0; minimum_friend_count = (unsigned int) opt; - opt = 16; - GNUNET_CONFIGURATION_get_value_number (cfg, - "TOPOLOGY", - "TARGET-CONNECTION-COUNT", - &opt); + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_number (cfg, + "TOPOLOGY", + "TARGET-CONNECTION-COUNT", + &opt)) + opt = 16; target_connection_count = (unsigned int) opt; if ( (friends_only == GNUNET_YES) || diff --git a/src/transport/gnunet-service-transport.c b/src/transport/gnunet-service-transport.c index d97cb15eb..2b1e82d21 100644 --- a/src/transport/gnunet-service-transport.c +++ b/src/transport/gnunet-service-transport.c @@ -2132,7 +2132,8 @@ plugin_env_receive (void *cls, _ ("Dropping incoming message due to repeated bandwidth quota violations.\n")); /* TODO: call stats */ - GNUNET_assert (NULL != service_context->neighbour); + GNUNET_assert ( (service_context == NULL) || + (NULL != service_context->neighbour) ); return service_context; } switch (ntohs (message->type)) diff --git a/src/util/configuration.c b/src/util/configuration.c index 769d2aadc..ff177d966 100644 --- a/src/util/configuration.c +++ b/src/util/configuration.c @@ -315,6 +315,49 @@ GNUNET_CONFIGURATION_write (struct GNUNET_CONFIGURATION_Handle *data, } +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; + } +} + + +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); +} + + +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; +} + + static struct ConfigSection * findSection (const struct GNUNET_CONFIGURATION_Handle *data, const char *section) { diff --git a/src/util/container_meta_data.c b/src/util/container_meta_data.c index b79de57d2..6265fc142 100644 --- a/src/util/container_meta_data.c +++ b/src/util/container_meta_data.c @@ -187,8 +187,9 @@ GNUNET_CONTAINER_meta_data_get_contents (const struct if (!EXTRACTOR_isBinaryType (md->items[i].type)) { if ((iterator != NULL) && - (GNUNET_OK != iterator (md->items[i].type, - md->items[i].data, closure))) + (GNUNET_OK != iterator (closure, + md->items[i].type, + md->items[i].data))) return GNUNET_SYSERR; } else diff --git a/src/util/crypto_ksk.c b/src/util/crypto_ksk.c index 45f11f8fe..170974b2f 100644 --- a/src/util/crypto_ksk.c +++ b/src/util/crypto_ksk.c @@ -790,7 +790,9 @@ entropy_generator (void *cls, { if (genproc != 0) { - PLIBC_KILL(genproc, SIGTERM); + if (0 != PLIBC_KILL(genproc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "kill"); GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc)); genproc = 0; } @@ -808,7 +810,9 @@ entropy_generator (void *cls, GNUNET_break (0); return; } - PLIBC_KILL(genproc, SIGTERM); + if (0 != PLIBC_KILL(genproc, SIGTERM)) + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "kill"); GNUNET_break (GNUNET_OK == GNUNET_OS_process_wait (genproc)); genproc = 0; } diff --git a/src/util/disk.c b/src/util/disk.c index 738839807..08f14caf6 100644 --- a/src/util/disk.c +++ b/src/util/disk.c @@ -234,7 +234,10 @@ GNUNET_DISK_mktemp (const char *template) GNUNET_free (fn); return NULL; } - CLOSE (fd); + if (0 != CLOSE (fd)) + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, + "close", + fn); return fn; } @@ -1043,6 +1046,7 @@ GNUNET_DISK_file_open (const char *fn, int flags, ...) else { GNUNET_break (0); + GNUNET_free (expfn); return NULL; } if (flags & GNUNET_DISK_OPEN_FAILIFEXISTS) -- cgit v1.2.3