summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--contrib/Makefile.am3
-rw-r--r--contrib/fcfsd/fcfsd-forbidden.html11
-rw-r--r--contrib/fcfsd/fcfsd-index.html345
-rw-r--r--contrib/fcfsd/fcfsd-notfound.html11
-rw-r--r--src/namestore/Makefile.am3
-rw-r--r--src/namestore/gnunet-namestore-fcfsd.c1594
6 files changed, 1105 insertions, 862 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am
index 331620586..f42fb684d 100644
--- a/contrib/Makefile.am
+++ b/contrib/Makefile.am
@@ -11,6 +11,9 @@ dist_pkgdata_DATA = \
gns/def.tex \
gns/gns-form-fields.xml \
gns/gns-form.xslt \
+ fcfsd/fcfsd-index.html \
+ fcfsd/fcfsd-notfound.html \
+ fcfsd/fcfsd-forbidden.html \
branding/logo/gnunet-logo.pdf \
branding/logo/gnunet-logo.png \
branding/logo/gnunet-logo-color.png \
diff --git a/contrib/fcfsd/fcfsd-forbidden.html b/contrib/fcfsd/fcfsd-forbidden.html
new file mode 100644
index 000000000..57ebb4c61
--- /dev/null
+++ b/contrib/fcfsd/fcfsd-forbidden.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Forbidden - GNUnet FCFS Authority Name Registration Service</title>
+ </head>
+ <body>
+ <h1>You can not access this resource.</h1>
+ </body>
+</html>
diff --git a/contrib/fcfsd/fcfsd-index.html b/contrib/fcfsd/fcfsd-index.html
new file mode 100644
index 000000000..3fa71d7c8
--- /dev/null
+++ b/contrib/fcfsd/fcfsd-index.html
@@ -0,0 +1,345 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>GNUnet FCFS Authority Name Registration Service</title>
+ <style>
+ html {
+ box-sizing: border-box;
+ font-family: sans-serif;
+ }
+
+ *, *:before, *:after {
+ box-sizing: inherit;
+ }
+
+ header {
+ width: 800px;
+ margin: 0 auto;
+ }
+
+ main {
+ width: 800px;
+ margin: 0 auto;
+ }
+
+ section h4 {
+ text-align: center;
+ width: 100%;
+ }
+
+ section input {
+ width: 100%;
+ padding: 8px 17px;
+ font-size: 1rem;
+ border: 1px solid #aaa;
+ border-radius: 7px;
+ background-color: white;
+ margin-bottom: 7px;
+ }
+
+ section input:focus {
+ box-shadow: 0px 0px 5px 3px lightblue;
+ }
+
+ section button {
+ font-size: 1rem;
+ font-weight: bold;
+ background-color: #8b008b;
+ color: white;
+ border: none;
+ padding: 7px;
+ }
+
+ section button:hover {
+ background-color: #bf00bf;
+ }
+
+ section button:disabled {
+ background-color: gray;
+ }
+
+ section h3 {
+ text-align: center;
+ width: 100%;
+ }
+
+ section small {
+ display: block;
+ margin-bottom: 5px;
+ }
+
+ .error-message {
+ color: red;
+ }
+
+ .success-message {
+ color: green;
+ }
+
+ @media screen and (max-width: 991px) {
+ header, main {
+ width: 100%;
+ }
+ }
+
+ footer {
+ margin-top: 30px;
+ text-align: center;
+ }
+
+ nav {
+ border-bottom: 1px solid black;
+ }
+
+ nav button {
+ font-size: 1rem;
+ font-weight: bold;
+ background-color: #ccc;
+ border: 1px solid black;
+ border-bottom: none;
+ border-top-right-radius: 7px;
+ border-top-left-radius: 7px;
+ padding: 7px;
+ }
+
+ nav button:hover {
+ background-color: #f0f0f0;
+ cursor: pointer;
+ }
+
+ nav button.selected {
+ background-color: #f0f0f0;
+ }
+ </style>
+ </head>
+ <body>
+ <header>
+ <h1>Name Registration Service</h1>
+ <p>Here you can register a name for your zone as part of this service's
+ delegated names.</p>
+ <p>The registration is based on a <em>First Come First Served</em>
+ policy, meaning a name is given to the first user requesting it.</p>
+ <p>Use the search bar below to see if your desired name is available and
+ then use the form to submit your registration request.</p>
+ </header>
+ <main>
+ <div class="form-container">
+ <nav>
+ <button id="tab-search">Search</button>
+ <button id="tab-register">Register</button>
+ </nav>
+ <section id="search-form">
+ <h4>Is your name available?</h4>
+ <h3 id="search-result-message"></h3>
+ <input id="search-name"
+ name="search-name"
+ type="text"
+ placeholder="Your name..."
+ autocomplete="name"
+ maxlength="63"
+ minlength="1">
+ <small class="error-message" id="search-name-error"></small>
+ <button>Search</button>
+ </section>
+ <section id="submit-form">
+ <h4>Submit a registration request</h4>
+ <h3 id="submit-result-message"></h3>
+ <input id="register-name"
+ name="register-name"
+ type="text"
+ placeholder="Your name..."
+ autocomplete="off"
+ maxlength="63"
+ minlength="1">
+ <input id="register-value"
+ name="register-value"
+ type="text"
+ placeholder="Your zone key..."
+ autocomplete="off"
+ minlength="1">
+ <small class="error-message" id="submit-error"></small>
+ <button>Submit</button>
+ </section>
+ </div>
+ </main>
+ <footer>
+ <a href="https://gnunet.org">GNUnet homepage</a>
+ </footer>
+ <script>
+ const buttons = document.querySelectorAll('nav button');
+ for (let i=0; i<buttons.length; ++i) {
+ buttons[i].onclick = function (e) {
+ let selected = document.querySelector('nav button.selected');
+ if (selected) {
+ selected.classList.toggle('selected');
+ }
+ e.target.classList.toggle('selected');
+
+ let show = '';
+ let hide = '';
+ if (e.target.id === 'tab-search') {
+ show = 'search-form';
+ hide = 'submit-form';
+ } else {
+ show = 'submit-form';
+ hide = 'search-form'
+ }
+
+ document.getElementById(hide).style.display = 'none';
+ document.getElementById(show).style.display = 'block';
+ };
+ }
+
+ buttons[0].click({target: buttons[0]});
+
+ const searchbutton = document.querySelector('#search-form button');
+ const submitbutton = document.querySelector('#submit-form button');
+
+ document.getElementById('search-name').onkeydown = function (e) {
+ if (e.key !== 'Enter') {
+ return;
+ }
+
+ searchbutton.click();
+ };
+
+ for (let n of ['register-name', 'register-value']) {
+ document.getElementById(n).onkeydown = function (e) {
+ if (e.key !== 'Enter') {
+ return;
+ }
+
+ submitbutton.click();
+ };
+ }
+
+ searchbutton.onclick = function (e) {
+ const searchname = document.getElementById('search-name');
+ const errormsg = document.getElementById('search-name-error');
+ const resultmsg = document.getElementById('search-result-message');
+
+ if (0 === searchname.value.length) {
+ errormsg.innerText = 'The field can not be empty';
+ searchname.setCustomValidity('The field can not be empty');
+ return;
+ }
+
+ if (-1 !== searchname.value.indexOf('.')) {
+ errormsg.innerText = 'The name can not contain dots';
+ searchname.setCustomValidity('The name can not contain dots');
+ return;
+ }
+
+ searchname.setCustomValidity('');
+ errormsg.innerText = '';
+
+ const name = searchname.value.toLowerCase();
+
+ searchbutton.disabled = true;
+ submitbutton.disabled = true;
+
+ fetch(`/search?name=${name}`)
+ .then(function (response) {
+ if (!response.ok) {
+ throw 'error';
+ }
+
+ return response.json()
+ })
+ .then(function (data) {
+ if ("true" === data.free) {
+ resultmsg.innerText = `'${name}' is available!`;
+ resultmsg.classList.add('success-message');
+ resultmsg.classList.remove('error-message');
+ } else {
+ resultmsg.innerText = `'${name}' is not available`;
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ }
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ })
+ .catch(function (error) {
+ resultmsg.innerText = 'An error occurred while processing your query';
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ console.error(error);
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ });
+ };
+
+ submitbutton.onclick = function (e) {
+ const registername = document.getElementById('register-name');
+ const registervalue = document.getElementById('register-value');
+ const errormsg = document.getElementById('submit-error');
+ const resultmsg = document.getElementById('submit-result-message');
+
+ let errors = 0;
+ let errs = [];
+
+ if (0 === registername.value.length) {
+ errs.push('The name field can not be empty');
+ registername.setCustomValidity('The name field can not be empty');
+ ++errors;
+ }
+ if (-1 !== registername.value.indexOf('.')) {
+ errs.push('The name can not contain dots');
+ registername.setCustomValidity('The name can not contain dots');
+ ++errors;
+ }
+ if (0 === registervalue.value.length) {
+ errs.push('The value field can not be empty');
+ registervalue.setCustomValidity('The value field can not be empty');
+ ++errors;
+ }
+
+ if (0 < errors) {
+ errormsg.innerHTML = 'The form contains invalid values:';
+ for (let e of errs) {
+ errormsg.innerHTML += '<br/>' + e;
+ }
+ return;
+ }
+
+ searchbutton.disabled = true;
+ submitbutton.disabled = true;
+
+ fetch('/register', {
+ method: 'POST',
+ cache: 'no-cache',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify({
+ name: registername.value,
+ key: registervalue.value,
+ }),
+ }).then(function (response) {
+ return response.json();
+ }).then(function (data) {
+ if (data.error === "false") {
+ resultmsg.innerText = `'${registername.value}' was registered successfully!`;
+ resultmsg.classList.add('success-message');
+ resultmsg.classList.remove('error-message');
+ } else {
+ resultmsg.innerText = `'${registername.value}' could not be registered! (${data.message})`;
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ }
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ }).catch(function (error) {
+ resultmsg.innerText = 'An error occurred while processing your query';
+ resultmsg.classList.remove('success-message');
+ resultmsg.classList.add('error-message');
+ console.error(error);
+ searchbutton.disabled = false;
+ submitbutton.disabled = false;
+ });
+ };
+ </script>
+ </body>
+</html>
diff --git a/contrib/fcfsd/fcfsd-notfound.html b/contrib/fcfsd/fcfsd-notfound.html
new file mode 100644
index 000000000..676bf4a9a
--- /dev/null
+++ b/contrib/fcfsd/fcfsd-notfound.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8"/>
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Not Found - GNUnet FCFS Authority Name Registration Service</title>
+ </head>
+ <body>
+ <h1>The requested resource could not be found</h1>
+ </body>
+</html>
diff --git a/src/namestore/Makefile.am b/src/namestore/Makefile.am
index df4e5d662..61bcfbaf8 100644
--- a/src/namestore/Makefile.am
+++ b/src/namestore/Makefile.am
@@ -182,7 +182,8 @@ gnunet_namestore_fcfsd_LDADD = $(MHD_LIBS) \
$(top_builddir)/src/identity/libgnunetidentity.la \
libgnunetnamestore.la \
$(top_builddir)/src/util/libgnunetutil.la \
- $(GN_LIBINTL)
+ $(top_builddir)/src/json/libgnunetjson.la \
+ $(GN_LIBINTL) -ljansson
gnunet_namestore_fcfsd_CFLAGS = $(MHD_CFLAGS) $(AM_CFLAGS)
diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c
index 313aea6fc..ec99d13e5 100644
--- a/src/namestore/gnunet-namestore-fcfsd.c
+++ b/src/namestore/gnunet-namestore-fcfsd.c
@@ -1,6 +1,6 @@
/*
This file is part of GNUnet.
- Copyright (C) 2012-2014 GNUnet e.V.
+ 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
@@ -17,19 +17,13 @@
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
- *
- * TODO:
- * - need to track active zone info requests so we can cancel them
- * during shutdown, right?
- * - the code currently contains a 'race' between checking that the
- * domain name is available and allocating it to the new public key
- * (should this race be solved by namestore or by fcfsd?)
- * - nicer error reporting to browser
*/
+
#include "platform.h"
#include <microhttpd.h>
#include "gnunet_util_lib.h"
@@ -37,735 +31,618 @@
#include "gnunet_gnsrecord_lib.h"
#include "gnunet_namestore_service.h"
#include "gnunet_mhd_compat.h"
+#include "gnunet_json_lib.h"
/**
- * Invalid method page.
- */
-#define METHOD_ERROR \
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>Illegal request</title></head><body>Go away.</body></html>"
-
-/**
- * Front page. (/)
- */
-#define MAIN_PAGE \
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>GNUnet FCFS Authority Name Registration Service</title></head><body><form action=\"S\" method=\"post\">What is your desired domain name? (at most 63 lowercase characters, no dots allowed.) <input type=\"text\" name=\"domain\" /> <p> What is your public key? (Copy from gnunet-setup.) <input type=\"text\" name=\"pkey\" /> <input type=\"submit\" value=\"Next\" /><br/><a href=./Zoneinfo> List of all registered names </a></body></html>"
-
-/**
- * Second page (/S)
- */
-#define SUBMIT_PAGE \
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>%s</title></head><body>%s</body></html>"
-
-/**
- * Fcfs zoneinfo page (/Zoneinfo)
- */
-#define ZONEINFO_PAGE \
- "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>FCFS Zoneinfo</title></head><body><h1> FCFS Zoneinfo </h1><table border=\"1\"><th>name</th><th>PKEY</th>%s</table></body></html>"
-
-#define FCFS_ZONEINFO_URL "/Zoneinfo"
-
-/**
- * Mime type for HTML pages.
- */
-#define MIME_HTML "text/html"
-
-/**
- * Name of our cookie.
- */
-#define COOKIE_NAME "namestore-fcfsd"
-
-#define DEFAULT_ZONEINFO_BUFSIZE 2048
-
-/**
- * Phases a request goes through.
+ * 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.
*/
-enum Phase
+struct StaticPage
{
/**
- * Start phase (parsing POST, checking).
- */
- RP_START = 0,
-
- /**
- * Lookup to see if the domain name is taken.
+ * Handle to file on disk.
*/
- RP_LOOKUP,
+ struct GNUNET_DISK_FileHandle *handle;
/**
- * Storing of the record.
+ * Size in bytes of the file.
*/
- RP_PUT,
+ uint64_t size;
/**
- * We're done with success.
+ * Cached response object to send to clients.
*/
- RP_SUCCESS,
-
- /**
- * Send failure message.
- */
- RP_FAIL
+ struct MHD_Response *response;
};
-
/**
- * Data kept per request.
+ * Structure containing some request-specific data.
*/
-struct Request
+struct RequestData
{
/**
- * Associated session.
- */
- // FIXME: struct Session *session;
-
- /**
- * Post processor handling form data (IF this is
- * a POST request).
+ * The connection this request was sent in.
*/
- struct MHD_PostProcessor *pp;
+ struct MHD_Connection *c;
/**
- * MHD Connection
- */
- struct MHD_Connection *con;
- /**
- * URL to serve in response to this POST (if this request
- * was a 'POST')
+ * Body of the response object.
*/
- const char *post_url;
+ char *body;
/**
- * Active request with the namestore.
+ * Length in bytes of the body.
*/
- struct GNUNET_NAMESTORE_QueueEntry *qe;
+ size_t body_length;
/**
- * Active lookup iterator
- * TODO: deprecate or fix lookup by label and use above member
+ * Response code.
*/
- struct GNUNET_NAMESTORE_ZoneIterator *lookup_it;
- /**
- * Active iteration with the namestore.
- */
- struct GNUNET_NAMESTORE_ZoneIterator *zi;
+ int code;
/**
- * Current processing phase.
+ * Task started to search for an entry in the namestore.
*/
- enum Phase phase;
+ struct GNUNET_NAMESTORE_QueueEntry *searching;
/**
- * Domain name submitted via form.
+ * Task started to iterate over the namestore.
*/
- char domain_name[64];
+ struct GNUNET_NAMESTORE_ZoneIterator *iterating;
/**
- * Public key submitted via form.
- */
- char public_key[128];
-
- struct GNUNET_IDENTITY_PublicKey pub;
-};
-
-/**
- * Zoneinfo request
- */
-struct ZoneinfoRequest
-{
- /**
- * List iterator
+ * Pointer used while processing POST data.
*/
- struct GNUNET_NAMESTORE_ZoneIterator *list_it;
+ void *ptr;
/**
- * Buffer
+ * Name requested to be registered.
*/
- char*zoneinfo;
+ char *register_name;
/**
- * Buffer length
+ * Key (encoded as a string) to be associated with the requested name.
*/
- size_t buf_len;
+ char *register_key;
/**
- * Buffer write offset
+ * Key to be associated with the requested name.
*/
- size_t write_offset;
+ struct GNUNET_IDENTITY_PublicKey key;
};
/**
- * MHD daemon reference.
+ * Name of the zone being managed.
*/
-static struct MHD_Daemon *httpd;
+static char *zone = NULL;
/**
- * Main HTTP task.
+ * The port the daemon is listening to for HTTP requests.
*/
-static struct GNUNET_SCHEDULER_Task *httpd_task;
+static unsigned long long port = 18080;
/**
- * Handle to the namestore.
+ * Connection with the namestore service.
*/
-static struct GNUNET_NAMESTORE_Handle *ns;
+static struct GNUNET_NAMESTORE_Handle *namestore = NULL;
/**
- * Private key for the fcfsd zone.
+ * Connection with the identity service.
*/
-static struct GNUNET_IDENTITY_PrivateKey fcfs_zone_pkey;
+static struct GNUNET_IDENTITY_Handle *identity = NULL;
/**
- * Connection to identity service.
+ * Private key of the zone.
*/
-static struct GNUNET_IDENTITY_Handle *identity;
+static const struct GNUNET_IDENTITY_PrivateKey *zone_key = NULL;
/**
- * Zoneinfo page we currently use.
+ * The HTTP daemon.
*/
-static struct MHD_Response *info_page;
+static struct MHD_Daemon *httpd = NULL;
/**
- * Task that runs #update_zoneinfo_page peridicially.
+ * Task executing the HTTP daemon.
*/
-static struct GNUNET_SCHEDULER_Task *uzp_task;
+static struct GNUNET_SCHEDULER_Task *httpd_task = NULL;
/**
- * Request for our ego.
+ * The main page, a.k.a. "index.html"
*/
-static struct GNUNET_IDENTITY_Operation *id_op;
+static struct StaticPage *main_page = NULL;
/**
- * Port we use for the HTTP server.
+ * Page indicating the requested resource could not be found.
*/
-static unsigned long long port;
+static struct StaticPage *notfound_page = NULL;
/**
- * Name of the zone we manage.
+ * Page indicating the requested resource could not be accessed, and other
+ * errors.
*/
-static char *zone;
-
+static struct StaticPage *forbidden_page = NULL;
/**
- * Task run whenever HTTP server operations are pending.
+ * Task ran at shutdown to clean up everything.
*
* @param cls unused
*/
static void
-do_httpd (void *cls);
+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 (main_page->response);
+ GNUNET_free (main_page->handle);
+ GNUNET_free (main_page);
+ }
+ if (NULL != forbidden_page)
+ {
+ MHD_destroy_response (main_page->response);
+ GNUNET_free (main_page->handle);
+ GNUNET_free (main_page);
+ }
+ if (NULL != namestore)
+ {
+ GNUNET_NAMESTORE_disconnect (namestore);
+ }
-/**
- * Schedule task to run MHD server now.
- */
-static void
-run_httpd_now ()
-{
- if (NULL != httpd_task)
+ if (NULL != identity)
{
- GNUNET_SCHEDULER_cancel (httpd_task);
- httpd_task = NULL;
+ GNUNET_IDENTITY_disconnect (identity);
}
- httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
}
-
/**
- * Create fresh version of zone information.
+ * Called when the HTTP server has some pending operations.
+ *
+ * @param cls unused
*/
static void
-update_zoneinfo_page (void *cls);
-
+do_httpd (void *cls);
/**
- * Function called on error in zone iteration.
+ * Schedule a task to run MHD.
*/
static void
-zone_iteration_error (void *cls)
+run_httpd (void)
{
- struct ZoneinfoRequest *zr = cls;
+ fd_set rs;
+ fd_set ws;
+ fd_set es;
- zr->list_it = NULL;
- GNUNET_free (zr->zoneinfo);
- GNUNET_SCHEDULER_cancel (uzp_task);
- uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
- NULL);
-}
+ 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);
+}
/**
- * Function called once the zone iteration is done.
+ * Called when the HTTP server has some pending operations.
+ *
+ * @param cls unused
*/
static void
-zone_iteration_end (void *cls)
+do_httpd (void *cls)
{
- struct ZoneinfoRequest *zr = cls;
- struct MHD_Response *response;
- char*full_page;
-
- zr->list_it = NULL;
-
- /* return static form */
- GNUNET_asprintf (&full_page,
- ZONEINFO_PAGE,
- zr->zoneinfo);
- response = MHD_create_response_from_buffer (strlen (full_page),
- (void *) full_page,
- MHD_RESPMEM_MUST_FREE);
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CONTENT_TYPE,
- MIME_HTML);
- MHD_destroy_response (info_page);
- info_page = response;
- GNUNET_free (zr->zoneinfo);
+ 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);
+}
/**
- * Process a record that was stored in the namestore, adding
- * the information to the HTML.
+ * Generate a JSON object.
*
- * @param cls closure with the `struct ZoneinfoRequest *`
- * @param zone_key private key of the zone; NULL on disconnect
- * @param name label of the records; NULL on disconnect
- * @param rd_len number of entries in @a rd array, 0 if label was deleted
- * @param rd array of records with data to store
+ * @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 void
-iterate_cb (void *cls,
- const struct GNUNET_IDENTITY_PrivateKey *zone_key,
- const char *name,
- unsigned int rd_len,
- const struct GNUNET_GNSRECORD_Data *rd)
+static char *
+make_json (const char *key, const char *value, ...)
{
- struct ZoneinfoRequest *zr = cls;
- size_t bytes_free;
- char*pkey;
- char*new_buf;
+ va_list args;
+ va_start(args, value);
+
+ json_t *obj = NULL;
- (void) zone_key;
- if (1 != rd_len)
+ obj = json_object ();
+ if (NULL == key || NULL == value)
{
- GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
- 1);
- return;
+ va_end (args);
+ return json_dumps (obj, JSON_COMPACT);
}
- if ((GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type) &&
- (GNUNET_GNSRECORD_TYPE_EDKEY != rd->record_type))
+ json_object_set (obj, key, json_string (value));
+
+ char *k = va_arg (args, char *);
+ if (NULL == k)
{
- GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
- 1);
- return;
+ va_end (args);
+ return json_dumps (obj, JSON_COMPACT);
}
-
- bytes_free = zr->buf_len - zr->write_offset;
- pkey = GNUNET_GNSRECORD_value_to_string (rd->record_type,
- rd->data,
- rd->data_size);
- if (NULL == pkey)
+ char *v = va_arg (args, char *);
+ if (NULL == v)
{
- GNUNET_break (0);
- GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
- 1);
- return;
+ va_end (args);
+ return json_dumps (obj, JSON_COMPACT);
}
- if (bytes_free < (strlen (name) + strlen (pkey) + 40))
+
+ while (NULL != k && NULL != v)
{
- new_buf = GNUNET_malloc (zr->buf_len * 2);
- GNUNET_memcpy (new_buf, zr->zoneinfo, zr->write_offset);
- GNUNET_free (zr->zoneinfo);
- zr->zoneinfo = new_buf;
- zr->buf_len *= 2;
+ json_object_set (obj, k, json_string (v));
+ k = va_arg (args, char *);
+ if (NULL != k)
+ {
+ v = va_arg (args, char *);
+ }
}
- sprintf (zr->zoneinfo + zr->write_offset,
- "<tr><td>%s</td><td>%s</td></tr>",
- name,
- pkey);
- zr->write_offset = strlen (zr->zoneinfo);
- GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
- 1);
- GNUNET_free (pkey);
-}
+ va_end (args);
-/**
- * Handler that returns FCFS zoneinfo page.
- *
- * @param connection connection to use
- */
-static int
-serve_zoneinfo_page (struct MHD_Connection *connection)
-{
- return MHD_queue_response (connection,
- MHD_HTTP_OK,
- info_page);
-}
+ char *json = json_dumps (obj, JSON_COMPACT);
+ json_decref (obj);
+ return json;
+}
/**
- * Create fresh version of zone information.
+ * The namestore search task failed.
+ *
+ * @param cls the request data
*/
static void
-update_zoneinfo_page (void *cls)
+search_error_cb (void *cls)
{
- static struct ZoneinfoRequest zr;
-
- (void) cls;
- uzp_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
- &update_zoneinfo_page,
- NULL);
- if (NULL != zr.list_it)
- return;
- zr.zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
- zr.buf_len = DEFAULT_ZONEINFO_BUFSIZE;
- zr.write_offset = 0;
- zr.list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
- &fcfs_zone_pkey,
- &zone_iteration_error,
- &zr,
- &iterate_cb,
- &zr,
- &zone_iteration_end,
- &zr);
+ 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 ();
}
-
/**
- * Handler that returns a simple static HTTP page.
+ * The lookup terminated with some results.
*
- * @param connection connection to use
- * @return #MHD_YES on success
+ * @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 int
-serve_main_page (struct MHD_Connection *connection)
+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)
{
- int ret;
- struct MHD_Response *response;
+ (void) zone;
+ (void) d;
- /* return static form */
- response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
- (void *) MAIN_PAGE,
- MHD_RESPMEM_PERSISTENT);
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CONTENT_TYPE,
- MIME_HTML);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- response);
- MHD_destroy_response (response);
- return ret;
-}
+ 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;
-/**
- * Send the 'SUBMIT_PAGE'.
- *
- * @param info information string to send to the user
- * @param request request information
- * @param connection connection to use
- */
-static int
-fill_s_reply (const char *info,
- struct Request *request,
- struct MHD_Connection *connection)
-{
- int ret;
- char *reply;
- struct MHD_Response *response;
-
- (void) request;
- GNUNET_asprintf (&reply,
- SUBMIT_PAGE,
- info,
- info);
- /* return static form */
- response = MHD_create_response_from_buffer (strlen (reply),
- (void *) reply,
- MHD_RESPMEM_MUST_FREE);
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CONTENT_TYPE,
- MIME_HTML);
- ret = MHD_queue_response (connection,
- MHD_HTTP_OK,
- response);
- MHD_destroy_response (response);
- return ret;
+ run_httpd_now ();
}
-
/**
- * Iterator over key-value pairs where the value
- * maybe made available in increments and/or may
- * not be zero-terminated. Used for processing
- * POST data.
+ * An error occurred while registering a name.
*
- * @param cls user-specified closure
- * @param kind type of the value
- * @param key 0-terminated key for the value
- * @param filename name of the uploaded file, NULL if not known
- * @param content_type mime-type of the data, NULL if not known
- * @param transfer_encoding encoding of the data, NULL if not known
- * @param data pointer to size bytes of data at the
- * specified offset
- * @param off offset of data in the overall value
- * @param size number of bytes in data available
- * @return #MHD_YES to continue iterating,
- * #MHD_NO to abort the iteration
+ * @param cls the connection
*/
-static MHD_RESULT
-post_iterator (void *cls,
- enum MHD_ValueKind kind,
- const char *key,
- const char *filename,
- const char *content_type,
- const char *transfer_encoding,
- const char *data,
- uint64_t off,
- size_t size)
+static void
+register_error_cb (void *cls)
{
- struct Request *request = cls;
-
- (void) kind;
- (void) filename;
- (void) content_type;
- (void) transfer_encoding;
- if (0 == strcmp ("domain", key))
- {
- if (size + off >= sizeof(request->domain_name))
- size = sizeof(request->domain_name) - off - 1;
- GNUNET_memcpy (&request->domain_name[off],
- data,
- size);
- request->domain_name[size + off] = '\0';
- return MHD_YES;
- }
- if (0 == strcmp ("pkey", key))
- {
- if (size + off >= sizeof(request->public_key))
- size = sizeof(request->public_key) - off - 1;
- GNUNET_memcpy (&request->public_key[off],
- data,
- size);
- request->public_key[size + off] = '\0';
- return MHD_YES;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _ ("Unsupported form value `%s'\n"),
- key);
- return MHD_YES;
+ struct RequestData *rd = cls;
+
+ MHD_resume_connection (rd->c);
+ rd->searching = NULL;
+ rd->body = make_json ("error", "true",
+ "message", _ ("unable to scan namestore"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ run_httpd_now ();
}
-
/**
- * Continuation called to notify client about result of the
- * operation.
+ * A name/key pair has been successfully registered, or maybe not.
*
- * @param cls closure
- * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
- * #GNUNET_NO if content was already there
- * #GNUNET_YES (or other positive value) on success
- * @param emsg NULL on success, otherwise an error message
+ * @param cls the connection
+ * @param status result of the operation
+ * @param emsg error message if any
*/
static void
-put_continuation (void *cls,
- int32_t success,
+register_done_cb (void *cls,
+ int32_t status,
const char *emsg)
{
- struct Request *request = cls;
+ struct RequestData *rd = cls;
+
+ MHD_resume_connection (rd->c);
+ rd->searching = NULL;
- request->qe = NULL;
- if (0 >= success)
+ if (GNUNET_SYSERR == status || GNUNET_NO == status)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- _ ("Failed to create record for domain `%s': %s\n"),
- request->domain_name,
+ _ ("Failed to create record for `%s': %s\n"),
+ rd->register_name,
emsg);
- request->phase = RP_FAIL;
+ rd->body = make_json ("error", "true",
+ "message", emsg);
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
}
else
- request->phase = RP_SUCCESS;
- MHD_resume_connection (request->con);
+ {
+ rd->body = make_json ("error", "false",
+ "message", _ ("no errors"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_OK;
+ }
+
run_httpd_now ();
}
-
/**
- * Function called if we had an error in zone-to-name mapping.
+ * 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
-zone_to_name_error (void *cls)
+register_do_cb (void *cls,
+ const struct GNUNET_IDENTITY_PrivateKey *key,
+ const char *label,
+ unsigned int count,
+ const struct GNUNET_GNSRECORD_Data *d)
{
- struct Request *request = cls;
+ (void) key;
+ (void) d;
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _ ("Error when mapping zone to name\n"));
- request->phase = RP_FAIL;
- MHD_resume_connection (request->con);
- run_httpd_now ();
-}
+ struct RequestData *rd = cls;
+ rd->searching = NULL;
-/**
- * Test if a name mapping was found, if so, refuse. If not, initiate storing of the record.
- *
- * @param cls closure
- * @param zone_key public key of the zone
- * @param name name that is being mapped (at most 255 characters long)
- * @param rd_count number of entries in @a rd array
- * @param rd array of records with data to store
- */
-static void
-zone_to_name_cb (void *cls,
- const struct GNUNET_IDENTITY_PrivateKey *zone_key,
- const char *name,
- unsigned int rd_count,
- const struct GNUNET_GNSRECORD_Data *rd)
-{
- struct Request *request = cls;
- struct GNUNET_GNSRECORD_Data r;
- char *rdata;
-
- (void) rd;
- (void) zone_key;
- request->qe = NULL;
- if (0 != rd_count)
+ if (0 != count)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Found existing name `%s' for the given key\n"),
- name);
- request->phase = RP_FAIL;
- MHD_resume_connection (request->con);
+ _ ("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"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_FORBIDDEN;
run_httpd_now ();
return;
}
- if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&request->pub,
- &rdata,
- &r.data_size,
- &r.record_type))
+
+ 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"));
- request->phase = RP_FAIL;
- MHD_resume_connection (request->con);
+ _ ("Error creating record data\n"));
+ MHD_resume_connection (rd->c);
+ rd->searching = NULL;
+ rd->body = make_json ("error", "true",
+ "message", _ ("unable to store record"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
run_httpd_now ();
return;
}
- r.data = rdata;
- r.expiration_time = UINT64_MAX;
- r.flags = GNUNET_GNSRECORD_RF_NONE;
- request->qe = GNUNET_NAMESTORE_records_store (ns,
- &fcfs_zone_pkey,
- request->domain_name,
- 1, &r,
- &put_continuation,
- request);
- GNUNET_free (rdata);
-}
+ gd.data = gdraw;
+ gd.expiration_time = UINT64_MAX;
+ gd.flags = GNUNET_GNSRECORD_RF_NONE;
+ rd->searching = GNUNET_NAMESTORE_records_store (namestore,
+ zone_key,
+ rd->register_name,
+ 1,
+ &gd,
+ &register_done_cb,
+ rd);
+
+ GNUNET_free (gdraw);
+}
/**
- * We encountered an error in the name lookup.
+ * An error occurred while iterating the namestore.
+ *
+ * @param cls the connection
*/
static void
-lookup_it_error (void *cls)
+iterate_error_cb (void *cls)
{
- struct Request *request = cls;
-
- MHD_resume_connection (request->con);
- request->qe = NULL;
- request->phase = RP_FAIL;
+ struct RequestData *rd = cls;
+
+ MHD_resume_connection (rd->c);
+ rd->iterating = NULL;
+ rd->body = make_json ("error", "true",
+ "message", _ ("unable to scan namestore"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
run_httpd_now ();
}
-
/**
- * We got a block back from the namestore. Decrypt it
- * and continue to process the result.
+ * A block was received from the namestore.
*
- * @param cls the 'struct Request' we are processing
- * @param zonekey private key of the zone; NULL on disconnect
- * @param label label of the records; NULL on disconnect
- * @param rd_count number of entries in @a rd array, 0 if label was deleted
- * @param rd array of records with data to store
+ * @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
-lookup_it_processor (void *cls,
- const struct GNUNET_IDENTITY_PrivateKey *zonekey,
- const char *label,
- unsigned int rd_count,
- const struct GNUNET_GNSRECORD_Data *rd)
+iterate_do_cb (void *cls,
+ const struct GNUNET_IDENTITY_PrivateKey *key,
+ const char *label,
+ unsigned int count,
+ const struct GNUNET_GNSRECORD_Data *d)
{
- struct Request *request = cls;
-
+ (void) key;
(void) label;
- (void) rd;
- (void) zonekey;
- if (0 == strcmp (label, request->domain_name))
+ (void) d;
+
+ struct RequestData *rd = cls;
+
+ if (0 == strcmp (label, rd->register_name))
{
- GNUNET_break (0 != rd_count);
+ GNUNET_break (0 != count);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Found %u existing records for domain `%s'\n"),
- rd_count,
- request->domain_name);
- request->phase = RP_FAIL;
+ _ ("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"));
+ 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 (request->lookup_it, 1);
-}
+ GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1);
+}
+/**
+ * All entries in the namestore have been iterated over.
+ *
+ * @param cls the connection
+ */
static void
-lookup_it_finished (void *cls)
+iterate_done_cb (void *cls)
{
- struct Request *request = cls;
-
- if (RP_FAIL == request->phase)
- {
- MHD_resume_connection (request->con);
- run_httpd_now ();
- return;
- }
- if (GNUNET_OK !=
- GNUNET_IDENTITY_public_key_from_string (request->public_key,
- &request->pub))
- {
- GNUNET_break (0);
- request->phase = RP_FAIL;
- MHD_resume_connection (request->con);
- run_httpd_now ();
- return;
- }
- request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
- &fcfs_zone_pkey,
- &request->pub,
- &zone_to_name_error,
- request,
- &zone_to_name_cb,
- request);
+ 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;
+}
/**
- * Main MHD callback for handling requests.
+ * Send a response back to a connected client.
*
* @param cls unused
- * @param connection MHD connection handle
- * @param url the requested url
- * @param method the HTTP method used ("GET", "PUT", etc.)
- * @param version the HTTP version string ("HTTP/1.1" for version 1.1, etc.)
- * @param upload_data the data being uploaded (excluding HEADERS,
- * for a POST that fits into memory and that is encoded
- * with a supported encoding, the POST data will NOT be
- * given in upload_data and is instead available as
- * part of MHD_get_connection_values; very large POST
- * data *will* be made available incrementally in
- * upload_data)
- * @param upload_data_size set initially to the size of the
- * @a upload_data provided; the method must update this
- * value to the number of bytes NOT processed;
- * @param ptr pointer to location where we store the 'struct Request'
- * @return #MHD_YES if the connection was handled successfully,
- * #MHD_NO if the socket must be closed due to a serious
- * error while handling the request
+ * @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,
@@ -777,300 +654,249 @@ create_response (void *cls,
size_t *upload_data_size,
void **ptr)
{
- struct MHD_Response *response;
- struct Request *request;
- struct GNUNET_IDENTITY_PublicKey pub;
- MHD_RESULT ret;
-
(void) cls;
(void) version;
- if ((0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
- (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)))
- {
- if (0 == strcmp (url, FCFS_ZONEINFO_URL))
- ret = serve_zoneinfo_page (connection);
- else
- ret = serve_main_page (connection);
- if (ret != MHD_YES)
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _ ("Failed to create page for `%s'\n"),
- url);
- return ret;
- }
- if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
+
+ struct RequestData *rd = *ptr;
+
+ if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
{
- request = *ptr;
- if (NULL == request)
+ /* Handle a previously suspended request */
+ if (NULL != rd)
+ {
+ return serve_json (rd->c, rd->body, rd->body_length, rd->code);
+ }
+
+ if (0 == strcmp ("/", url))
{
- request = GNUNET_new (struct Request);
- request->con = connection;
- *ptr = request;
- request->pp = MHD_create_post_processor (connection,
- 1024,
- &post_iterator,
- request);
- if (NULL == request->pp)
+ 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)
{
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- _ ("Failed to setup post processor for `%s'\n"),
- url);
- return MHD_NO; /* internal error */
+ 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;
}
- if (NULL != request->pp)
+
+ 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))
{
- /* evaluate POST data */
- MHD_post_process (request->pp,
- upload_data,
- *upload_data_size);
- if (0 != *upload_data_size)
+ /* Handle a previously suspended request */
+ if (NULL != rd && NULL != rd->body)
{
- *upload_data_size = 0;
- return MHD_YES;
+ return serve_json (rd->c, rd->body, rd->body_length, rd->code);
}
- /* done with POST data, serve response */
- MHD_destroy_post_processor (request->pp);
- request->pp = NULL;
- }
- if (GNUNET_OK !=
- GNUNET_IDENTITY_public_key_from_string (request->public_key,
- &pub))
- {
- /* parse error */
- return fill_s_reply ("Failed to parse given public key",
- request, connection);
- }
- switch (request->phase)
- {
- case RP_START:
- if (NULL != strchr (request->domain_name, (int) '.'))
+
+ if (NULL == rd)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Domain name must not contain `.'\n"));
- request->phase = RP_FAIL;
- return fill_s_reply ("Domain name must not contain `.', sorry.",
- request,
- connection);
+ rd = GNUNET_new (struct RequestData);
+ rd->c = connection;
+ rd->body = NULL;
+ rd->ptr = NULL;
+ *ptr = rd;
}
- if (NULL != strchr (request->domain_name, (int) '+'))
+
+ 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)
{
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- _ ("Domain name must not contain `+'\n"));
- request->phase = RP_FAIL;
- return fill_s_reply ("Domain name must not contain `+', sorry.",
- request, connection);
+ 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"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE;
+ return MHD_YES;
+ case GNUNET_JSON_PR_JSON_INVALID:
+ rd->body = make_json ("error", "true",
+ "message", _ ("the submitted data is invalid"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_BAD_REQUEST;
+ return MHD_YES;
+ default:
+ break;
}
- request->phase = RP_LOOKUP;
- MHD_suspend_connection (request->con);
- request->lookup_it
- = GNUNET_NAMESTORE_zone_iteration_start (ns,
- &fcfs_zone_pkey,
- &lookup_it_error,
- request,
- &lookup_it_processor,
- request,
- &lookup_it_finished,
- request);
- break;
-
- case RP_LOOKUP:
- break;
-
- case RP_PUT:
- break;
-
- case RP_FAIL:
- return fill_s_reply ("Request failed, sorry.",
- request, connection);
-
- case RP_SUCCESS:
- return fill_s_reply ("Success.",
- request, connection);
-
- default:
- GNUNET_break (0);
- return MHD_NO;
- }
- return MHD_YES; /* will have a reply later... */
- }
- /* unsupported HTTP method */
- response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
- (void *) METHOD_ERROR,
- MHD_RESPMEM_PERSISTENT);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NOT_ACCEPTABLE,
- response);
- MHD_destroy_response (response);
- return ret;
-}
+ /* POST data has been read in its entirety */
-/**
- * Callback called upon completion of a request.
- * Decrements session reference counter.
- *
- * @param cls not used
- * @param connection connection that completed
- * @param con_cls session handle
- * @param toe status code
- */
-static void
-request_completed_callback (void *cls,
- struct MHD_Connection *connection,
- void **con_cls,
- enum MHD_RequestTerminationCode toe)
-{
- struct Request *request = *con_cls;
+ 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"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_BAD_REQUEST;
+ return MHD_YES;
+ }
- (void) cls;
- (void) connection;
- (void) toe;
- if (NULL == request)
- return;
- if (NULL != request->pp)
- MHD_destroy_post_processor (request->pp);
- if (NULL != request->qe)
- GNUNET_NAMESTORE_cancel (request->qe);
- GNUNET_free (request);
-}
+ rd->register_name = strdup (name);
+ rd->register_key = strdup (key);
+ json_decref (json);
+ GNUNET_JSON_post_parser_cleanup (rd->ptr);
-#define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
+ if (NULL != strchr (rd->register_name, '.') ||
+ NULL != strchr (rd->register_name, '+'))
+ {
+ rd->body = make_json ("error", "true",
+ "message", _ ("invalid name"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_BAD_REQUEST;
+ }
+ 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"));
+ rd->body_length = strlen (rd->body);
+ rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ return MHD_YES;
+ }
-/**
- * Schedule tasks to run MHD server.
- */
-static void
-run_httpd ()
-{
- fd_set rs;
- fd_set ws;
- fd_set es;
- struct GNUNET_NETWORK_FDSet *wrs;
- struct GNUNET_NETWORK_FDSet *wws;
- struct GNUNET_NETWORK_FDSet *wes;
- int max;
- int haveto;
- UNSIGNED_MHD_LONG_LONG timeout;
- struct GNUNET_TIME_Relative tv;
+ 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;
+ }
- FD_ZERO (&rs);
- FD_ZERO (&ws);
- FD_ZERO (&es);
- wrs = GNUNET_NETWORK_fdset_create ();
- wes = GNUNET_NETWORK_fdset_create ();
- wws = GNUNET_NETWORK_fdset_create ();
- max = -1;
- GNUNET_assert (MHD_YES ==
- MHD_get_fdset (httpd,
- &rs,
- &ws,
- &es,
- &max));
- haveto = MHD_get_timeout (httpd,
- &timeout);
- if (haveto == MHD_YES)
- tv.rel_value_us = (uint64_t) timeout * 1000LL;
- else
- tv = GNUNET_TIME_UNIT_FOREVER_REL;
- GNUNET_NETWORK_fdset_copy_native (wrs,
- &rs,
- max + 1);
- GNUNET_NETWORK_fdset_copy_native (wws,
- &ws,
- max + 1);
- GNUNET_NETWORK_fdset_copy_native (wes,
- &es,
- max + 1);
- httpd_task =
- GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
- tv,
- wrs,
- wws,
- &do_httpd,
- NULL);
- GNUNET_NETWORK_fdset_destroy (wrs);
- GNUNET_NETWORK_fdset_destroy (wws);
- GNUNET_NETWORK_fdset_destroy (wes);
-}
+ return MHD_queue_response (connection,
+ MHD_HTTP_FORBIDDEN,
+ forbidden_page->response);
+ }
+ return MHD_queue_response (connection,
+ MHD_HTTP_NOT_IMPLEMENTED,
+ forbidden_page->response);
+}
/**
- * Task run whenever HTTP server operations are pending.
+ * Called when a request is completed.
*
* @param cls unused
+ * @param connection the connection
+ * @param ptr connection-specific data
+ * @param status status code
*/
static void
-do_httpd (void *cls)
+completed_cb (void *cls,
+ struct MHD_Connection *connection,
+ void **ptr,
+ enum MHD_RequestTerminationCode status)
{
(void) cls;
- httpd_task = NULL;
- MHD_run (httpd);
- run_httpd ();
-}
+ (void) connection;
+ (void) status;
+ struct RequestData *rd = *ptr;
-/**
- * Task run on shutdown. Cleans up everything.
- *
- * @param cls unused
- */
-static void
-do_shutdown (void *cls)
-{
- (void) cls;
- if (NULL != httpd_task)
+ if (NULL == rd)
{
- GNUNET_SCHEDULER_cancel (httpd_task);
- httpd_task = NULL;
+ return;
}
- if (NULL != uzp_task)
+
+ if (NULL == rd->body)
{
- GNUNET_SCHEDULER_cancel (uzp_task);
- uzp_task = NULL;
+ GNUNET_free (rd->body);
}
- if (NULL != ns)
+
+ if (NULL != rd->searching)
{
- GNUNET_NAMESTORE_disconnect (ns);
- ns = NULL;
+ GNUNET_NAMESTORE_cancel (rd->searching);
}
- if (NULL != httpd)
+
+ if (NULL != rd->register_name)
{
- MHD_stop_daemon (httpd);
- httpd = NULL;
+ GNUNET_free (rd->register_name);
}
- if (NULL != id_op)
+
+ if (NULL != rd->register_key)
{
- GNUNET_IDENTITY_cancel (id_op);
- id_op = NULL;
+ GNUNET_free (rd->register_key);
}
- if (NULL != identity)
+
+ if (NULL != rd->iterating)
{
- GNUNET_IDENTITY_disconnect (identity);
- identity = NULL;
+ GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
}
-}
+ GNUNET_free (rd);
+}
/**
- * Method called to inform about the egos of this peer.
- *
- * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get, this
- * function is only called ONCE, and 'NULL' being passed in @a ego does
- * indicate an error (for example because name is taken or no default value is
- * known). If @a ego is non-NULL and if '*ctx' is set in those callbacks, the
- * value WILL be passed to a subsequent call to the identity callback of
- * #GNUNET_IDENTITY_connect (if that one was not NULL).
+ * Called for each ego provided by the identity service.
*
- * @param cls closure, NULL
- * @param ego ego handle
- * @param ctx context for application to store data for this ego
- * (during the lifetime of this process, initially NULL)
- * @param name name assigned by the user for this ego,
- * NULL if the user just deleted the ego and it
- * must thus no longer be used
+ * @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,
@@ -1078,15 +904,14 @@ identity_cb (void *cls,
void **ctx,
const char *name)
{
- int options;
-
(void) cls;
(void) ctx;
- if (NULL == name)
- return;
- if (0 != strcmp (name,
- zone))
+
+ if (NULL == name || 0 != strcmp (name, zone))
+ {
return;
+ }
+
if (NULL == ego)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1094,30 +919,24 @@ identity_cb (void *cls,
GNUNET_SCHEDULER_shutdown ();
return;
}
- fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
- options = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
+ 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 (options,
+ httpd = MHD_start_daemon (flags,
(uint16_t) port,
NULL, NULL,
&create_response, NULL,
- MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
- MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned
- int) 1,
- MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
- MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4
- *
- 1024),
- MHD_OPTION_NOTIFY_COMPLETED,
- &request_completed_callback, 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);
- if (MHD_USE_DEBUG == options)
- break;
- options = MHD_USE_DEBUG;
- }
- while (NULL == httpd);
+ flags = MHD_USE_DEBUG;
+ } while (NULL == httpd && flags != MHD_USE_DEBUG);
+
if (NULL == httpd)
{
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -1125,105 +944,158 @@ identity_cb (void *cls,
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 #GNUNET_SYSERR 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;
+}
/**
- * Main function that will be run.
+ * Called after the service is up.
*
* @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be NULL!)
- * @param cfg configuration
+ * @param args remaining command line arguments
+ * @param cfgfile name of the configuration file
+ * @param cfg the service configuration
*/
static void
-run (void *cls,
- char *const *args,
- const char *cfgfile,
- const struct GNUNET_CONFIGURATION_Handle *cfg)
+run_service (void *cls,
+ char *const *args,
+ const char *cfgfile,
+ const struct GNUNET_CONFIGURATION_Handle *cfg)
{
(void) cls;
(void) args;
(void) cfgfile;
- if (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_number (cfg,
- "fcfsd",
- "HTTPPORT",
- &port))
+
+ GNUNET_log_setup ("fcfsd", "WARNING", NULL);
+
+ if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
+ "fcfsd",
+ "HTTPPORT",
+ &port))
{
- GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
- "fcfsd", "HTTPPORT");
- return;
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ _ ("No port specified, using default value\n"));
}
- ns = GNUNET_NAMESTORE_connect (cfg);
- if (NULL == ns)
+
+ 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);
+
+ 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;
}
- uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
- NULL);
- GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
- NULL);
-}
+ 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 for the fcfs daemon.
+ * The main function of the fcfs daemon.
*
* @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
+ * @parsm argv the command line argumens
+ * @return 0 successful exit, a different value otherwise
*/
int
-main (int argc,
- char *const *argv)
+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 that is to be managed by FCFSD"),
- &zone)),
+ (GNUNET_GETOPT_option_string ('z',
+ "zone",
+ "EGO",
+ gettext_noop ("name of the zone managed by FCFSD"),
+ &zone)),
GNUNET_GETOPT_OPTION_END
};
- int ret;
-
- if (GNUNET_OK !=
- GNUNET_STRINGS_get_utf8_args (argc, argv,
- &argc, &argv))
- return 2;
-
- GNUNET_log_setup ("fcfsd",
- "WARNING",
- NULL);
- ret =
- (GNUNET_OK ==
- GNUNET_PROGRAM_run (argc,
- argv,
- "gnunet-namestore-fcfsd",
- _ (
- "GNU Name System First Come First Serve name registration service"),
- options,
- &run, NULL)) ? 0 : 1;
- GNUNET_free_nz ((void *) argv);
- // FIXME
- // GNUNET_CRYPTO_ecdsa_key_clear (&fcfs_zone_pkey);
- return ret;
-}
-
-/* end of gnunet-namestore-fcfsd.c */
+ 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);
+}