gnunet-gns-registrar

GNU Name System registrar
Log | Files | Refs | README

commit 2d9148508662517464efd36116fc5e48d7118d1e
Author: Martin Schanzenbach <schanzen@gnunet.org>
Date:   Sat, 30 Sep 2023 09:07:10 +0200

Initial commit

Diffstat:
Ameson.build | 10++++++++++
Asrc/meson.build | 1+
Asrc/service/gnunet-namestore-fcfsd.c | 1160+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/service/meson.build | 3+++
4 files changed, 1174 insertions(+), 0 deletions(-)

diff --git a/meson.build b/meson.build @@ -0,0 +1,10 @@ +project('gns-registrar', 'c') +gnunetdeps = [dependency('libmicrohttpd'), + dependency('gnunetutil'), + dependency('gnunetnamestore'), + dependency('jansson'), + dependency('gnunetjson'), + dependency('gnunetgnsrecord'), + dependency('gnunetidentity'), + dependency('gnunetgns')] +subdir('src') diff --git a/src/meson.build b/src/meson.build @@ -0,0 +1 @@ +subdir('service') diff --git a/src/service/gnunet-namestore-fcfsd.c b/src/service/gnunet-namestore-fcfsd.c @@ -0,0 +1,1160 @@ +/* + This file is part of GNUnet. + Copyright (C) 2012-2021 GNUnet e.V. + + GNUnet is free software: you can redistribute it and/or modify it + under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, + or (at your option) any later version. + + GNUnet is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + + SPDX-License-Identifier: AGPL3.0-or-later + */ + +/** + * @file gnunet-namestore-fcfsd.c + * @brief HTTP daemon that offers first-come-first-serve GNS domain registration + * @author Christian Grothoff + */ + +#include <microhttpd.h> +#include "gnunet/platform.h" +#include "gnunet/gnunet_util_lib.h" +#include "gnunet/gnunet_identity_service.h" +#include "gnunet/gnunet_gnsrecord_lib.h" +#include "gnunet/gnunet_namestore_service.h" +#include "gnunet/gnunet_mhd_compat.h" +#include "gnunet/gnunet_json_lib.h" + +/** + * Structure representing a static page. + * "Static" means that the server does not process the page before sending it + * to the client. Clients can still process the received data, for example + * because there are scripting elements within. + */ +struct StaticPage +{ + /** + * Handle to file on disk. + */ + struct GNUNET_DISK_FileHandle *handle; + + /** + * Size in bytes of the file. + */ + uint64_t size; + + /** + * Cached response object to send to clients. + */ + struct MHD_Response *response; +}; + +/** + * Structure containing some request-specific data. + */ +struct RequestData +{ + /** + * The connection this request was sent in. + */ + struct MHD_Connection *c; + + /** + * Body of the response object. + */ + char *body; + + /** + * Length in bytes of the body. + */ + size_t body_length; + + /** + * Response code. + */ + int code; + + /** + * Task started to search for an entry in the namestore. + */ + struct GNUNET_NAMESTORE_QueueEntry *searching; + + /** + * Task started to iterate over the namestore. + */ + struct GNUNET_NAMESTORE_ZoneIterator *iterating; + + /** + * Pointer used while processing POST data. + */ + void *ptr; + + /** + * Name requested to be registered. + */ + char *register_name; + + /** + * Key (encoded as a string) to be associated with the requested name. + */ + char *register_key; + + /** + * Key to be associated with the requested name. + */ + struct GNUNET_IDENTITY_PublicKey key; +}; + +/** + * Name of the zone being managed. + */ +static char *zone = NULL; + +/** + * The port the daemon is listening to for HTTP requests. + */ +static unsigned long long port = 18080; + +/** + * Connection with the namestore service. + */ +static struct GNUNET_NAMESTORE_Handle *namestore = NULL; + +/** + * Connection with the identity service. + */ +static struct GNUNET_IDENTITY_Handle *identity = NULL; + +/** + * Private key of the zone. + */ +static const struct GNUNET_IDENTITY_PrivateKey *zone_key = NULL; + +/** + * The HTTP daemon. + */ +static struct MHD_Daemon *httpd = NULL; + +/** + * Task executing the HTTP daemon. + */ +static struct GNUNET_SCHEDULER_Task *httpd_task = NULL; + +/** + * The main page, a.k.a. "index.html" + */ +static struct StaticPage *main_page = NULL; + +/** + * Page indicating the requested resource could not be found. + */ +static struct StaticPage *notfound_page = NULL; + +/** + * Page indicating the requested resource could not be accessed, and other + * errors. + */ +static struct StaticPage *forbidden_page = NULL; + +/** + * The relative expiration time for added records + */ +static struct GNUNET_TIME_Relative record_exp; + +/** + * Task ran at shutdown to clean up everything. + * + * @param cls unused + */ +static void +do_shutdown (void *cls) +{ + /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so + calling `GNUNET_DISK_file_close' would generate a spurious warning message + in the log. Since that function does nothing but close the descriptor and + free the allocated memory, After destroying the response all that's left to + do is call `GNUNET_free'. */ + if (NULL != main_page) + { + MHD_destroy_response (main_page->response); + GNUNET_free (main_page->handle); + GNUNET_free (main_page); + } + if (NULL != notfound_page) + { + MHD_destroy_response (notfound_page->response); + GNUNET_free (notfound_page->handle); + GNUNET_free (notfound_page); + } + if (NULL != forbidden_page) + { + MHD_destroy_response (forbidden_page->response); + GNUNET_free (forbidden_page->handle); + GNUNET_free (forbidden_page); + } + + if (NULL != namestore) + { + GNUNET_NAMESTORE_disconnect (namestore); + } + + if (NULL != identity) + { + GNUNET_IDENTITY_disconnect (identity); + } + + if (NULL != httpd_task) + { + GNUNET_SCHEDULER_cancel (httpd_task); + } + if (NULL != httpd) + { + MHD_stop_daemon (httpd); + } +} + + +/** + * Called when the HTTP server has some pending operations. + * + * @param cls unused + */ +static void +do_httpd (void *cls); + +/** + * Schedule a task to run MHD. + */ +static void +run_httpd (void) +{ + fd_set rs; + fd_set ws; + fd_set es; + + struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create (); + struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create (); + struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create (); + + FD_ZERO (&rs); + FD_ZERO (&ws); + FD_ZERO (&es); + + int max = -1; + GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max)); + + unsigned MHD_LONG_LONG timeout = 0; + struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL; + if (MHD_YES == MHD_get_timeout (httpd, &timeout)) + { + gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, + timeout); + } + + GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1); + GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1); + GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1); + + httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, + gtime, + grs, + gws, + &do_httpd, + NULL); + GNUNET_NETWORK_fdset_destroy (grs); + GNUNET_NETWORK_fdset_destroy (gws); + GNUNET_NETWORK_fdset_destroy (ges); +} + + +/** + * Called when the HTTP server has some pending operations. + * + * @param cls unused + */ +static void +do_httpd (void *cls) +{ + httpd_task = NULL; + MHD_run (httpd); + run_httpd (); +} + + +static void +run_httpd_now (void) +{ + if (NULL != httpd_task) + { + GNUNET_SCHEDULER_cancel (httpd_task); + httpd_task = NULL; + } + httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL); +} + + +/** + * Generate a JSON object. + * + * @param key the key for the first element + * @param value the value for the first element + * @param ... key-value pairs of the object, terminated by NULL + * @return a JSON string (allocated) + */ +static char * +make_json (const char *key, const char *value, ...) +{ + va_list args; + va_start (args, value); + + json_t *obj = NULL; + + obj = json_object (); + if ((NULL == key) || (NULL == value)) + { + va_end (args); + return json_dumps (obj, JSON_COMPACT); + } + + json_object_set (obj, key, json_string (value)); + + char *k = va_arg (args, char *); + if (NULL == k) + { + va_end (args); + return json_dumps (obj, JSON_COMPACT); + } + char *v = va_arg (args, char *); + if (NULL == v) + { + va_end (args); + return json_dumps (obj, JSON_COMPACT); + } + + while (NULL != k && NULL != v) + { + json_object_set (obj, k, json_string (v)); + k = va_arg (args, char *); + if (NULL != k) + { + v = va_arg (args, char *); + } + } + + va_end (args); + + char *json = json_dumps (obj, JSON_COMPACT); + json_decref (obj); + + return json; +} + + +/** + * The namestore search task failed. + * + * @param cls the request data + */ +static void +search_error_cb (void *cls) +{ + struct RequestData *rd = cls; + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("can not search the namestore"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); +} + + +/** + * The lookup terminated with some results. + * + * @param cls closure + * @param zone the private key of the zone + * @param label the result label + * @param count number of records found + * @param d records found + */ +static void +search_done_cb (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *zone, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *d) +{ + (void) zone; + (void) d; + + struct RequestData *rd = cls; + MHD_resume_connection (rd->c); + + rd->searching = NULL; + rd->body = make_json ("error", "false", + "free", (0 == count) ? "true" : "false", + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_OK; + + run_httpd_now (); +} + + +/** + * An error occurred while registering a name. + * + * @param cls the connection + */ +static void +register_error_cb (void *cls) +{ + struct RequestData *rd = cls; + + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("unable to scan namestore"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); +} + + +static void +register_done_cb (void *cls, + enum GNUNET_ErrorCode ec) +{ + struct RequestData *rd = cls; + + MHD_resume_connection (rd->c); + rd->searching = NULL; + + if (GNUNET_EC_NONE != ec) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Failed to create record for `%s': %s\n"), + rd->register_name, + GNUNET_ErrorCode_get_hint (ec)); + rd->body = make_json ("error", "true", + "message", + GNUNET_ErrorCode_get_hint (ec), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + } + else + { + rd->body = make_json ("error", "false", + "message", _ ("no errors"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_OK; + } + + run_httpd_now (); +} + + +/** + * Attempt to register the requested name. + * + * @param cls the connection + * @param key the zone key + * @param label name of the record + * @param count number of records found + * @param d records + */ +static void +register_do_cb (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *key, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *d) +{ + (void) key; + (void) d; + + struct RequestData *rd = cls; + + rd->searching = NULL; + + if (0 != count) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("The requested key `%s' exists as `%s'\n"), + rd->register_key, + label); + + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("key exists"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_FORBIDDEN; + run_httpd_now (); + return; + } + + struct GNUNET_GNSRECORD_Data gd; + char *gdraw = NULL; + + if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key), + &gdraw, + &(gd.data_size), + &(gd.record_type))) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Error creating record data\n")); + MHD_resume_connection (rd->c); + rd->searching = NULL; + rd->body = make_json ("error", "true", + "message", _ ("unable to store record"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); + return; + } + + gd.data = gdraw; + gd.expiration_time = record_exp.rel_value_us; + gd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION; + + rd->searching = GNUNET_NAMESTORE_records_store (namestore, + zone_key, + rd->register_name, + 1, + &gd, + &register_done_cb, + rd); + + GNUNET_free (gdraw); +} + + +/** + * An error occurred while iterating the namestore. + * + * @param cls the connection + */ +static void +iterate_error_cb (void *cls) +{ + struct RequestData *rd = cls; + + MHD_resume_connection (rd->c); + rd->iterating = NULL; + rd->body = make_json ("error", "true", + "message", _ ("unable to scan namestore"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + run_httpd_now (); +} + + +/** + * A block was received from the namestore. + * + * @param cls the connection + * @param key the zone key + * @param label the records' label + * @param count number of records found + * @param d the found records + */ +static void +iterate_do_cb (void *cls, + const struct GNUNET_IDENTITY_PrivateKey *key, + const char *label, + unsigned int count, + const struct GNUNET_GNSRECORD_Data *d) +{ + (void) key; + (void) label; + (void) d; + + struct RequestData *rd = cls; + + if (0 == strcmp (label, rd->register_name)) + { + GNUNET_break (0 != count); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("Requested name `%s' exists with `%u' records\n"), + rd->register_name, + count); + + MHD_resume_connection (rd->c); + rd->body = make_json ("error", "true", + "message", _ ("name exists\n"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_FORBIDDEN; + GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating); + run_httpd_now (); + return; + } + + GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1); +} + + +/** + * All entries in the namestore have been iterated over. + * + * @param cls the connection + */ +static void +iterate_done_cb (void *cls) +{ + struct RequestData *rd = cls; + + rd->iterating = NULL; + + /* See if the key was not registered already */ + rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore, + zone_key, + &(rd->key), + &register_error_cb, + rd, + &register_do_cb, + rd); +} + + +/** + * Generate a response containing JSON and send it to the client. + * + * @param c the connection + * @param body the response body + * @param length the body length in bytes + * @param code the response code + * @return MHD_NO on error + */ +static MHD_RESULT +serve_json (struct MHD_Connection *c, + char *body, + size_t length, + int code) +{ + struct MHD_Response *response = + MHD_create_response_from_buffer (length, + body, + MHD_RESPMEM_PERSISTENT); + MHD_RESULT r = MHD_queue_response (c, code, response); + MHD_destroy_response (response); + return r; +} + + +/** + * Send a response back to a connected client. + * + * @param cls unused + * @param connection the connection with the client + * @param url the requested address + * @param method the HTTP method used + * @param version the protocol version (including the "HTTP/" part) + * @param upload_data data sent with a POST request + * @param upload_data_size length in bytes of the POST data + * @param ptr used to pass data between request handling phases + * @return MHD_NO on error + */ +static MHD_RESULT +create_response (void *cls, + struct MHD_Connection *connection, + const char *url, + const char *method, + const char *version, + const char *upload_data, + size_t *upload_data_size, + void **ptr) +{ + (void) cls; + (void) version; + + struct RequestData *rd = *ptr; + + if (0 == strcmp (method, MHD_HTTP_METHOD_GET)) + { + /* Handle a previously suspended request */ + if (NULL != rd) + { + return serve_json (rd->c, rd->body, rd->body_length, rd->code); + } + + if (0 == strcmp ("/", url)) + { + return MHD_queue_response (connection, + MHD_HTTP_OK, + main_page->response); + } + + if (0 == strcmp ("/search", url)) + { + const char *name = MHD_lookup_connection_value (connection, + MHD_GET_ARGUMENT_KIND, + "name"); + if (NULL == name) + { + return MHD_queue_response (connection, + MHD_HTTP_BAD_REQUEST, + forbidden_page->response); + } + + MHD_suspend_connection (connection); + rd = GNUNET_new (struct RequestData); + rd->c = connection; + rd->searching = GNUNET_NAMESTORE_records_lookup (namestore, + zone_key, + name, + &search_error_cb, + rd, + &search_done_cb, + rd); + *ptr = rd; + return MHD_YES; + } + + return MHD_queue_response (connection, + MHD_HTTP_NOT_FOUND, + notfound_page->response); + } + + if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) + { + /* We take a shortcut here by always serving the main page: starting a + namestore lookup, allocating the necessary resources, waiting for the + lookup to complete and then discard everything just because it was a HEAD + and thus only the headers are significative, is an unnecessary waste of + resources. The handling of this method could be smarter, for example by + sending a proper content type header based on the endpoint, but this is + not a service in which HEAD requests are significant, so there's no need + to spend too much time here. */ + return MHD_queue_response (connection, + MHD_HTTP_OK, + main_page->response); + } + + if (0 == strcmp (method, MHD_HTTP_METHOD_POST)) + { + if (0 == strcmp ("/register", url)) + { + /* Handle a previously suspended request */ + if ((NULL != rd) && (NULL != rd->body)) + { + return serve_json (rd->c, rd->body, rd->body_length, rd->code); + } + + if (NULL == rd) + { + rd = GNUNET_new (struct RequestData); + rd->c = connection; + rd->body = NULL; + rd->ptr = NULL; + *ptr = rd; + } + + json_t *json = NULL; + enum GNUNET_JSON_PostResult result = + GNUNET_JSON_post_parser (32 * 1024, + connection, + &(rd->ptr), + upload_data, + upload_data_size, + &json); + + switch (result) + { + case GNUNET_JSON_PR_CONTINUE: + /* Keep processing POST data */ + return MHD_YES; + case GNUNET_JSON_PR_OUT_OF_MEMORY: + case GNUNET_JSON_PR_REQUEST_TOO_LARGE: + rd->body = make_json ("error", "true", + "message", _ ("unable to process submitted data"), + NULL); + rd->body_length = strlen (rd->body); +#ifdef MHD_HTTP_CONTENT_TOO_LARGE + rd->code = MHD_HTTP_CONTENT_TOO_LARGE; +#else + rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE; +#endif + return MHD_YES; + case GNUNET_JSON_PR_JSON_INVALID: + rd->body = make_json ("error", "true", + "message", _ ("the submitted data is invalid"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_BAD_REQUEST; + return MHD_YES; + default: + break; + } + + /* POST data has been read in its entirety */ + + const char *name = json_string_value (json_object_get (json, "name")); + const char *key = json_string_value (json_object_get (json, "key")); + if ((NULL == name) || (NULL == key) || (0 == strlen (name)) || (0 == + strlen ( + key))) + { + json_decref (json); + rd->body = make_json ("error", "true", + "message", _ ("invalid parameters"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_BAD_REQUEST; + return MHD_YES; + } + + rd->register_name = strdup (name); + rd->register_key = strdup (key); + + json_decref (json); + GNUNET_JSON_post_parser_cleanup (rd->ptr); + + if ((NULL != strchr (rd->register_name, '.')) || + (NULL != strchr (rd->register_name, '+'))) + { + rd->body = make_json ("error", "true", + "message", _ ("invalid name"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_BAD_REQUEST; + return MHD_YES; + } + + if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (rd->register_key, + &(rd->key))) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + _ ("Unable to parse key %s\n"), + rd->register_key); + + rd->body = make_json ("error", "true", + "message", _ ("unable to parse key"), + NULL); + rd->body_length = strlen (rd->body); + rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR; + return MHD_YES; + } + + MHD_suspend_connection (connection); + /* See if the requested name is free */ + rd->iterating = + GNUNET_NAMESTORE_zone_iteration_start (namestore, + zone_key, + &iterate_error_cb, + rd, + &iterate_do_cb, + rd, + &iterate_done_cb, + rd); + return MHD_YES; + } + + return MHD_queue_response (connection, + MHD_HTTP_FORBIDDEN, + forbidden_page->response); + } + + return MHD_queue_response (connection, + MHD_HTTP_NOT_IMPLEMENTED, + forbidden_page->response); +} + + +/** + * Called when a request is completed. + * + * @param cls unused + * @param connection the connection + * @param ptr connection-specific data + * @param status status code + */ +static void +completed_cb (void *cls, + struct MHD_Connection *connection, + void **ptr, + enum MHD_RequestTerminationCode status) +{ + (void) cls; + (void) connection; + (void) status; + + struct RequestData *rd = *ptr; + + if (NULL == rd) + { + return; + } + + if (NULL == rd->body) + { + GNUNET_free (rd->body); + } + + if (NULL != rd->searching) + { + GNUNET_NAMESTORE_cancel (rd->searching); + } + + if (NULL != rd->register_name) + { + GNUNET_free (rd->register_name); + } + + if (NULL != rd->register_key) + { + GNUNET_free (rd->register_key); + } + + if (NULL != rd->iterating) + { + GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating); + } + + GNUNET_free (rd); +} + + +/** + * Called for each ego provided by the identity service. + * + * @param cls closure + * @param ego the ego + * @param ctx application-provided data for the ego + * @param name the ego name + */ +static void +identity_cb (void *cls, + struct GNUNET_IDENTITY_Ego *ego, + void **ctx, + const char *name) +{ + (void) cls; + (void) ctx; + + if ((NULL == name) || (0 != strcmp (name, zone))) + { + return; + } + + if (NULL == ego) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("No ego configured for `fcfsd` subsystem\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + zone_key = GNUNET_IDENTITY_ego_get_private_key (ego); + + int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME; + do + { + httpd = MHD_start_daemon (flags, + (uint16_t) port, + NULL, NULL, + &create_response, NULL, + MHD_OPTION_CONNECTION_LIMIT, 128, + MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1, + MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024, + MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL, + MHD_OPTION_END); + flags = MHD_USE_DEBUG; + } while (NULL == httpd && flags != MHD_USE_DEBUG); + + if (NULL == httpd) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to start HTTP server\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + run_httpd (); +} + + +/** + * Open a file on disk and generate a response object for it. + * + * @param name name of the file to open + * @param basedir directory where the file is located + * @return NULL on error + */ +static struct StaticPage * +open_static_page (const char *name, const char *basedir) +{ + char *fullname = NULL; + GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name); + + struct GNUNET_DISK_FileHandle *f = + GNUNET_DISK_file_open (fullname, + GNUNET_DISK_OPEN_READ, + GNUNET_DISK_PERM_NONE); + GNUNET_free (fullname); + + if (NULL == f) + { + return NULL; + } + + off_t size = 0; + if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size)) + { + GNUNET_DISK_file_close (f); + return NULL; + } + + struct MHD_Response *response = + MHD_create_response_from_fd64 (size, + f->fd); + + if (NULL == response) + { + GNUNET_DISK_file_close (f); + return NULL; + } + + struct StaticPage *page = GNUNET_new (struct StaticPage); + page->handle = f; + page->size = (uint64_t) size; + page->response = response; + return page; +} + + +/** + * Called after the service is up. + * + * @param cls closure + * @param args remaining command line arguments + * @param cfgfile name of the configuration file + * @param cfg the service configuration + */ +static void +run_service (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *cfg) +{ + (void) cls; + (void) args; + (void) cfgfile; + + GNUNET_log_setup ("fcfsd", "WARNING", NULL); + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg, + "fcfsd", + "RELATIVE_RECORD_EXPIRATION", + &record_exp)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _("No expiration specified for records.\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg, + "fcfsd", + "HTTPPORT", + &port)) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + _ ("No port specified, using default value\n")); + } + + GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL); + + namestore = GNUNET_NAMESTORE_connect (cfg); + if (NULL == namestore) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to connect to namestore\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL); + if (NULL == identity) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Failed to connect to identity\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } + + char *basedir = NULL; + if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, + "fcfsd", + "HTMLDIR", + &basedir)) + { + basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + } + + main_page = open_static_page ("index.html", basedir); + notfound_page = open_static_page ("notfound.html", basedir); + forbidden_page = open_static_page ("forbidden.html", basedir); + + GNUNET_free (basedir); + + if ((NULL == main_page) || (NULL == notfound_page) || (NULL == + forbidden_page) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + _ ("Unable to set up the daemon\n")); + GNUNET_SCHEDULER_shutdown (); + return; + } +} + + +/** + * The main function of the fcfs daemon. + * + * @param argc number of arguments from the command line + * @param argv the command line arguments + * @return 0 successful exit, a different value otherwise + */ +int +main (int argc, char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_mandatory + (GNUNET_GETOPT_option_string ('z', + "zone", + "EGO", + gettext_noop ( + "name of the zone managed by FCFSD"), + &zone)), + GNUNET_GETOPT_OPTION_END + }; + + return ((GNUNET_OK == GNUNET_PROGRAM_run (argc, + argv, + "gnunet-namestore-fcfsd", + _ ( + "GNU Name System First-Come-First-Served name registration service"), + options, + &run_service, + NULL)) ? + 0 : + 1); +} diff --git a/src/service/meson.build b/src/service/meson.build @@ -0,0 +1,3 @@ +executable('gnunet-namestore-fcfsd', + 'gnunet-namestore-fcfsd.c', + dependencies: gnunetdeps)