2 This file is part of GNUnet.
3 (C) 2012-2013 Christian Grothoff (and other contributing authors)
5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 3, or (at your
8 option) any later version.
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with GNUnet; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
21 * @file gnunet-namestore-fcfsd.c
22 * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23 * @author Christian Grothoff
26 * - the code currently contains a 'race' between checking that the
27 * domain name is available and allocating it to the new public key
28 * (should this race be solved by namestore or by fcfsd?)
29 * - nicer error reporting to browser
32 #include <microhttpd.h>
33 #include "gnunet_util_lib.h"
34 #include "gnunet_identity_service.h"
35 #include "gnunet_namestore_service.h"
38 * Invalid method page.
40 #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>"
45 #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>"
50 #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>"
53 * Fcfs zoneinfo page (/Zoneinfo)
55 #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>"
57 #define FCFS_ZONEINFO_URL "/Zoneinfo"
60 * Mime type for HTML pages.
62 #define MIME_HTML "text/html"
67 #define COOKIE_NAME "namestore-fcfsd"
69 #define DEFAULT_ZONEINFO_BUFSIZE 2048
72 * Phases a request goes through.
77 * Start phase (parsing POST, checking).
82 * Lookup to see if the domain name is taken.
87 * Storing of the record.
92 * We're done with success.
97 * Send failure message.
104 * Data kept per request.
110 * Associated session.
112 struct Session *session;
115 * Post processor handling form data (IF this is
118 struct MHD_PostProcessor *pp;
121 * URL to serve in response to this POST (if this request
124 const char *post_url;
127 * Active request with the namestore.
129 struct GNUNET_NAMESTORE_QueueEntry *qe;
132 * Current processing phase.
137 * Domain name submitted via form.
139 char domain_name[64];
142 * Public key submitted via form.
144 char public_key[128];
151 struct ZoneinfoRequest
156 struct MHD_Connection *connection;
161 struct GNUNET_NAMESTORE_ZoneIterator *list_it;
174 * Buffer write offset
180 * MHD deamon reference.
182 static struct MHD_Daemon *httpd;
187 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
190 * Handle to the namestore.
192 static struct GNUNET_NAMESTORE_Handle *ns;
195 * Private key for the fcfsd zone.
197 static struct GNUNET_CRYPTO_EccPrivateKey fcfs_zone_pkey;
200 * Connection to identity service.
202 static struct GNUNET_IDENTITY_Handle *identity;
205 * Request for our ego.
207 static struct GNUNET_IDENTITY_Operation *id_op;
210 * Port we use for the HTTP server.
212 static unsigned long long port;
216 * Task run whenever HTTP server operations are pending.
219 * @param tc scheduler context
223 const struct GNUNET_SCHEDULER_TaskContext *tc);
227 * Schedule task to run MHD server now.
232 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
234 GNUNET_SCHEDULER_cancel (httpd_task);
235 httpd_task = GNUNET_SCHEDULER_NO_TASK;
237 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
242 iterate_cb (void *cls,
243 const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
246 const struct GNUNET_NAMESTORE_RecordData *rd)
248 struct ZoneinfoRequest *zr = cls;
249 struct MHD_Response *response;
260 /* return static form */
261 GNUNET_asprintf (&full_page,
265 response = MHD_create_response_from_buffer (strlen (full_page),
267 MHD_RESPMEM_MUST_FREE);
268 MHD_add_response_header (response,
269 MHD_HTTP_HEADER_CONTENT_TYPE,
271 MHD_queue_response (zr->connection,
274 MHD_destroy_response (response);
275 GNUNET_free (zr->zoneinfo);
283 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
287 if (GNUNET_NAMESTORE_TYPE_PKEY != rd->record_type)
289 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
293 bytes_free = zr->buf_len - zr->write_offset;
294 pkey = GNUNET_NAMESTORE_value_to_string (rd->record_type,
298 if (bytes_free < (strlen (name) + strlen (pkey) + 40))
300 new_buf = GNUNET_malloc (zr->buf_len * 2);
301 memcpy (new_buf, zr->zoneinfo, zr->write_offset);
302 GNUNET_free (zr->zoneinfo);
303 zr->zoneinfo = new_buf;
306 sprintf (zr->zoneinfo + zr->write_offset,
307 "<tr><td>%s</td><td>%s</td></tr>",
310 zr->write_offset = strlen (zr->zoneinfo);
311 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
318 * Handler that returns FCFS zoneinfo page.
320 * @param connection connection to use
321 * @return MHD_YES on success
324 serve_zoneinfo_page (struct MHD_Connection *connection)
326 struct ZoneinfoRequest *zr;
328 zr = GNUNET_new (struct ZoneinfoRequest);
329 zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
330 zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE;
331 zr->connection = connection;
332 zr->write_offset = 0;
333 zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
342 * Handler that returns a simple static HTTP page.
344 * @param connection connection to use
345 * @return MHD_YES on success
348 serve_main_page (struct MHD_Connection *connection)
351 struct MHD_Response *response;
353 /* return static form */
354 response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
356 MHD_RESPMEM_PERSISTENT);
357 MHD_add_response_header (response,
358 MHD_HTTP_HEADER_CONTENT_TYPE,
360 ret = MHD_queue_response (connection,
363 MHD_destroy_response (response);
369 * Send the 'SUBMIT_PAGE'.
371 * @param info information string to send to the user
372 * @param request request information
373 * @param connection connection to use
376 fill_s_reply (const char *info,
377 struct Request *request,
378 struct MHD_Connection *connection)
382 struct MHD_Response *response;
384 GNUNET_asprintf (&reply,
388 /* return static form */
389 response = MHD_create_response_from_buffer (strlen (reply),
391 MHD_RESPMEM_MUST_FREE);
392 MHD_add_response_header (response,
393 MHD_HTTP_HEADER_CONTENT_TYPE,
395 ret = MHD_queue_response (connection,
398 MHD_destroy_response (response);
404 * Iterator over key-value pairs where the value
405 * maybe made available in increments and/or may
406 * not be zero-terminated. Used for processing
409 * @param cls user-specified closure
410 * @param kind type of the value
411 * @param key 0-terminated key for the value
412 * @param filename name of the uploaded file, NULL if not known
413 * @param content_type mime-type of the data, NULL if not known
414 * @param transfer_encoding encoding of the data, NULL if not known
415 * @param data pointer to size bytes of data at the
417 * @param off offset of data in the overall value
418 * @param size number of bytes in data available
419 * @return MHD_YES to continue iterating,
420 * MHD_NO to abort the iteration
423 post_iterator (void *cls,
424 enum MHD_ValueKind kind,
426 const char *filename,
427 const char *content_type,
428 const char *transfer_encoding,
429 const char *data, uint64_t off, size_t size)
431 struct Request *request = cls;
433 if (0 == strcmp ("domain", key))
435 if (size + off >= sizeof(request->domain_name))
436 size = sizeof (request->domain_name) - off - 1;
437 memcpy (&request->domain_name[off],
440 request->domain_name[size+off] = '\0';
443 if (0 == strcmp ("pkey", key))
445 if (size + off >= sizeof(request->public_key))
446 size = sizeof (request->public_key) - off - 1;
447 memcpy (&request->public_key[off],
450 request->public_key[size+off] = '\0';
453 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
454 _("Unsupported form value `%s'\n"),
463 * Continuation called to notify client about result of the
467 * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
468 * GNUNET_NO if content was already there
469 * GNUNET_YES (or other positive value) on success
470 * @param emsg NULL on success, otherwise an error message
473 put_continuation (void *cls,
477 struct Request *request = cls;
482 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
483 _("Failed to create record for domain `%s': %s\n"),
484 request->domain_name,
486 request->phase = RP_FAIL;
489 request->phase = RP_SUCCESS;
495 * Test if a name mapping was found, if so, refuse. If not, initiate storing of the record.
498 * @param zone_key public key of the zone
499 * @param name name that is being mapped (at most 255 characters long)
500 * @param rd_count number of entries in 'rd' array
501 * @param rd array of records with data to store
504 zone_to_name_cb (void *cls,
505 const struct GNUNET_CRYPTO_EccPrivateKey *zone_key,
507 unsigned int rd_count,
508 const struct GNUNET_NAMESTORE_RecordData *rd)
510 struct Request *request = cls;
511 struct GNUNET_NAMESTORE_RecordData r;
512 struct GNUNET_CRYPTO_ShortHashCode pub;
517 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
518 _("Found existing name `%s' for the given key\n"),
520 request->phase = RP_FAIL;
525 r.data_size = sizeof (pub);
526 r.expiration_time = UINT64_MAX;
527 r.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
528 r.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
529 request->qe = GNUNET_NAMESTORE_records_store (ns,
531 request->domain_name,
539 * Process a record that was stored in the namestore. Used to check if
540 * the requested name already exists in the namestore. If not,
541 * proceed to check if the requested key already exists.
544 * @param rd_count number of entries in 'rd' array
545 * @param rd array of records with data to store
548 lookup_result_processor (void *cls,
549 unsigned int rd_count,
550 const struct GNUNET_NAMESTORE_RecordData *rd)
552 struct Request *request = cls;
553 struct GNUNET_CRYPTO_EccPublicKey pub;
557 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
558 _("Found %u existing records for domain `%s'\n"),
560 request->domain_name);
561 request->phase = RP_FAIL;
566 GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
567 strlen (request->public_key),
571 request->phase = RP_FAIL;
575 request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
584 * We got a block back from the namestore. Decrypt it
585 * and continue to process the result.
587 * @param cls the 'struct Request' we are processing
588 * @param block block returned form namestore, NULL on error
591 lookup_block_processor (void *cls,
592 const struct GNUNET_NAMESTORE_Block *block)
594 struct Request *request = cls;
595 struct GNUNET_CRYPTO_EccPublicKey pub;
600 lookup_result_processor (request, 0, NULL);
603 GNUNET_CRYPTO_ecc_key_get_public (&fcfs_zone_pkey,
606 GNUNET_NAMESTORE_block_decrypt (block,
608 request->domain_name,
609 &lookup_result_processor,
613 request->phase = RP_FAIL;
621 * Main MHD callback for handling requests.
624 * @param connection MHD connection handle
625 * @param url the requested url
626 * @param method the HTTP method used ("GET", "PUT", etc.)
627 * @param version the HTTP version string (i.e. "HTTP/1.1")
628 * @param upload_data the data being uploaded (excluding HEADERS,
629 * for a POST that fits into memory and that is encoded
630 * with a supported encoding, the POST data will NOT be
631 * given in upload_data and is instead available as
632 * part of MHD_get_connection_values; very large POST
633 * data *will* be made available incrementally in
635 * @param upload_data_size set initially to the size of the
636 * upload_data provided; the method must update this
637 * value to the number of bytes NOT processed;
638 * @param ptr pointer to location where we store the 'struct Request'
639 * @return MHD_YES if the connection was handled successfully,
640 * MHD_NO if the socket must be closed due to a serious
641 * error while handling the request
644 create_response (void *cls,
645 struct MHD_Connection *connection,
649 const char *upload_data,
650 size_t *upload_data_size,
653 struct MHD_Response *response;
654 struct Request *request;
656 struct GNUNET_CRYPTO_EccPublicKey pub;
657 struct GNUNET_HashCode query;
659 if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
660 (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
662 if (0 == strcmp (url, FCFS_ZONEINFO_URL))
663 ret = serve_zoneinfo_page (connection);
665 ret = serve_main_page (connection);
667 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
668 _("Failed to create page for `%s'\n"),
672 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
677 request = GNUNET_malloc (sizeof (struct Request));
679 request->pp = MHD_create_post_processor (connection, 1024,
680 &post_iterator, request);
681 if (NULL == request->pp)
683 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
684 _("Failed to setup post processor for `%s'\n"),
686 return MHD_NO; /* internal error */
690 if (NULL != request->pp)
692 /* evaluate POST data */
693 MHD_post_process (request->pp,
696 if (0 != *upload_data_size)
698 *upload_data_size = 0;
701 /* done with POST data, serve response */
702 MHD_destroy_post_processor (request->pp);
706 GNUNET_CRYPTO_ecc_public_key_from_string (request->public_key,
707 strlen (request->public_key),
711 return fill_s_reply ("Failed to parse given public key",
712 request, connection);
714 switch (request->phase)
717 if (NULL != strchr (request->domain_name, (int) '.'))
719 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
720 _("Domain name must not contain `.'\n"));
721 request->phase = RP_FAIL;
722 return fill_s_reply ("Domain name must not contain `.', sorry.",
723 request, connection);
725 if (NULL != strchr (request->domain_name, (int) '+'))
727 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
728 _("Domain name must not contain `+'\n"));
729 request->phase = RP_FAIL;
730 return fill_s_reply ("Domain name must not contain `+', sorry.",
731 request, connection);
733 request->phase = RP_LOOKUP;
734 GNUNET_CRYPTO_ecc_key_get_public (&fcfs_zone_pkey,
736 GNUNET_NAMESTORE_query_from_public_key (&pub,
737 request->domain_name,
739 request->qe = GNUNET_NAMESTORE_lookup_block (ns,
741 &lookup_block_processor,
749 return fill_s_reply ("Request failed, sorry.",
750 request, connection);
752 return fill_s_reply ("Success.",
753 request, connection);
758 return MHD_YES; /* will have a reply later... */
760 /* unsupported HTTP method */
761 response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
762 (void *) METHOD_ERROR,
763 MHD_RESPMEM_PERSISTENT);
764 ret = MHD_queue_response (connection,
765 MHD_HTTP_METHOD_NOT_ACCEPTABLE,
767 MHD_destroy_response (response);
773 * Callback called upon completion of a request.
774 * Decrements session reference counter.
776 * @param cls not used
777 * @param connection connection that completed
778 * @param con_cls session handle
779 * @param toe status code
782 request_completed_callback (void *cls,
783 struct MHD_Connection *connection,
785 enum MHD_RequestTerminationCode toe)
787 struct Request *request = *con_cls;
791 if (NULL != request->pp)
792 MHD_destroy_post_processor (request->pp);
793 if (NULL != request->qe)
794 GNUNET_NAMESTORE_cancel (request->qe);
795 GNUNET_free (request);
799 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
803 * Schedule tasks to run MHD server.
811 struct GNUNET_NETWORK_FDSet *wrs;
812 struct GNUNET_NETWORK_FDSet *wws;
813 struct GNUNET_NETWORK_FDSet *wes;
816 UNSIGNED_MHD_LONG_LONG timeout;
817 struct GNUNET_TIME_Relative tv;
822 wrs = GNUNET_NETWORK_fdset_create ();
823 wes = GNUNET_NETWORK_fdset_create ();
824 wws = GNUNET_NETWORK_fdset_create ();
826 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
827 haveto = MHD_get_timeout (httpd, &timeout);
828 if (haveto == MHD_YES)
829 tv.rel_value_us = (uint64_t) timeout * 1000LL;
831 tv = GNUNET_TIME_UNIT_FOREVER_REL;
832 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
833 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
834 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
836 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
839 GNUNET_NETWORK_fdset_destroy (wrs);
840 GNUNET_NETWORK_fdset_destroy (wws);
841 GNUNET_NETWORK_fdset_destroy (wes);
846 * Task run whenever HTTP server operations are pending.
849 * @param tc scheduler context
853 const struct GNUNET_SCHEDULER_TaskContext *tc)
855 httpd_task = GNUNET_SCHEDULER_NO_TASK;
862 * Task run on shutdown. Cleans up everything.
865 * @param tc scheduler context
868 do_shutdown (void *cls,
869 const struct GNUNET_SCHEDULER_TaskContext *tc)
871 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
873 GNUNET_SCHEDULER_cancel (httpd_task);
874 httpd_task = GNUNET_SCHEDULER_NO_TASK;
878 GNUNET_NAMESTORE_disconnect (ns);
883 MHD_stop_daemon (httpd);
888 GNUNET_IDENTITY_cancel (id_op);
891 if (NULL != identity)
893 GNUNET_IDENTITY_disconnect (identity);
900 * Method called to inform about the egos of this peer.
902 * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
903 * this function is only called ONCE, and 'NULL' being passed in
904 * @a ego does indicate an error (i.e. name is taken or no default
905 * value is known). If @a ego is non-NULL and if '*ctx'
906 * is set in those callbacks, the value WILL be passed to a subsequent
907 * call to the identity callback of #GNUNET_IDENTITY_connect (if
908 * that one was not NULL).
910 * @param cls closure, NULL
911 * @param ego ego handle
912 * @param ctx context for application to store data for this ego
913 * (during the lifetime of this process, initially NULL)
914 * @param name name assigned by the user for this ego,
915 * NULL if the user just deleted the ego and it
916 * must thus no longer be used
919 identity_cb (void *cls,
920 struct GNUNET_IDENTITY_Ego *ego,
927 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
928 _("No ego configured for `fcfsd` subsystem\n"));
931 fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
932 httpd = MHD_start_daemon (MHD_USE_DUAL_STACK | MHD_USE_DEBUG,
935 &create_response, NULL,
936 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
937 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
938 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
939 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024),
940 MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
944 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
945 _("Failed to start HTTP server\n"));
946 GNUNET_SCHEDULER_shutdown ();
954 * Main function that will be run.
957 * @param args remaining command-line arguments
958 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
959 * @param cfg configuration
962 run (void *cls, char *const *args, const char *cfgfile,
963 const struct GNUNET_CONFIGURATION_Handle *cfg)
966 GNUNET_CONFIGURATION_get_value_number (cfg,
971 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
972 "fcfsd", "HTTPPORT");
975 ns = GNUNET_NAMESTORE_connect (cfg);
978 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
979 _("Failed to connect to namestore\n"));
982 identity = GNUNET_IDENTITY_connect (cfg,
984 id_op = GNUNET_IDENTITY_get (identity, "fcfsd",
986 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
992 * The main function for the fcfs daemon.
994 * @param argc number of arguments from the command line
995 * @param argv command line arguments
996 * @return 0 ok, 1 on error
999 main (int argc, char *const *argv)
1001 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1002 GNUNET_GETOPT_OPTION_END
1007 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1010 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1013 GNUNET_PROGRAM_run (argc, argv, "fcfsd",
1014 _("GNU Name System First Come First Serve name registration service"),
1016 &run, NULL)) ? 0 : 1;
1017 GNUNET_free ((void*) argv);
1021 /* end of gnunet-namestore-fcfsd.c */