2 This file is part of GNUnet.
3 (C) 2012 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-gns-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
30 * - figure out where this binary should go (is gns the right directory!?)
33 #include <gnunet_util_lib.h>
34 #include <microhttpd.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 "gns-fcfs"
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.
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 * Hash of the public key of the fcfsd zone.
197 static struct GNUNET_CRYPTO_ShortHashCode fcfsd_zone;
200 * Private key for the fcfsd zone.
202 static struct GNUNET_CRYPTO_RsaPrivateKey *fcfs_zone_pkey;
206 * Task run whenever HTTP server operations are pending.
209 * @param tc scheduler context
213 const struct GNUNET_SCHEDULER_TaskContext *tc);
217 * Schedule task to run MHD server now.
222 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
224 GNUNET_SCHEDULER_cancel (httpd_task);
225 httpd_task = GNUNET_SCHEDULER_NO_TASK;
227 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
231 iterate_cb (void *cls,
232 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
233 struct GNUNET_TIME_Absolute expire,
236 const struct GNUNET_NAMESTORE_RecordData *rd,
237 const struct GNUNET_CRYPTO_RsaSignature *signature)
239 struct ZoneinfoRequest *zr = cls;
240 struct MHD_Response *response;
251 /* return static form */
252 GNUNET_asprintf (&full_page,
256 response = MHD_create_response_from_buffer (strlen (full_page),
258 MHD_RESPMEM_MUST_FREE);
259 MHD_add_response_header (response,
260 MHD_HTTP_HEADER_CONTENT_TYPE,
262 MHD_queue_response (zr->connection,
265 MHD_destroy_response (response);
266 GNUNET_free (zr->zoneinfo);
274 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
278 if (GNUNET_NAMESTORE_TYPE_PKEY != rd->record_type)
280 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
284 bytes_free = zr->buf_len - zr->write_offset;
285 pkey = GNUNET_NAMESTORE_value_to_string (rd->record_type,
289 if (bytes_free < (strlen (name) + strlen (pkey) + 40))
291 new_buf = GNUNET_malloc (zr->buf_len * 2);
292 memcpy (new_buf, zr->zoneinfo, zr->write_offset);
293 GNUNET_free (zr->zoneinfo);
294 zr->zoneinfo = new_buf;
297 sprintf (zr->zoneinfo + zr->write_offset,
298 "<tr><td>%s</td><td>%s</td></tr>",
301 zr->write_offset = strlen (zr->zoneinfo);
302 GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
309 * Handler that returns FCFS zoneinfo page.
311 * @param connection connection to use
312 * @return MHD_YES on success
315 serve_zoneinfo_page (struct MHD_Connection *connection)
317 struct ZoneinfoRequest *zr;
319 zr = GNUNET_malloc (sizeof (struct ZoneinfoRequest));
321 zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
322 zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE;
323 zr->connection = connection;
324 zr->write_offset = 0;
326 printf ("adsadad1!\n");
327 zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
329 GNUNET_NAMESTORE_RF_RELATIVE_EXPIRATION,
330 GNUNET_NAMESTORE_RF_PRIVATE,
339 * Handler that returns a simple static HTTP page.
341 * @param connection connection to use
342 * @return MHD_YES on success
345 serve_main_page (struct MHD_Connection *connection)
348 struct MHD_Response *response;
350 /* return static form */
351 response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
353 MHD_RESPMEM_PERSISTENT);
354 MHD_add_response_header (response,
355 MHD_HTTP_HEADER_CONTENT_TYPE,
357 ret = MHD_queue_response (connection,
360 MHD_destroy_response (response);
366 * Send the 'SUBMIT_PAGE'.
368 * @param info information string to send to the user
369 * @param request request information
370 * @param connection connection to use
373 fill_s_reply (const char *info,
374 struct Request *request,
375 struct MHD_Connection *connection)
379 struct MHD_Response *response;
381 GNUNET_asprintf (&reply,
385 /* return static form */
386 response = MHD_create_response_from_buffer (strlen (reply),
388 MHD_RESPMEM_MUST_FREE);
389 MHD_add_response_header (response,
390 MHD_HTTP_HEADER_CONTENT_TYPE,
392 ret = MHD_queue_response (connection,
395 MHD_destroy_response (response);
401 * Iterator over key-value pairs where the value
402 * maybe made available in increments and/or may
403 * not be zero-terminated. Used for processing
406 * @param cls user-specified closure
407 * @param kind type of the value
408 * @param key 0-terminated key for the value
409 * @param filename name of the uploaded file, NULL if not known
410 * @param content_type mime-type of the data, NULL if not known
411 * @param transfer_encoding encoding of the data, NULL if not known
412 * @param data pointer to size bytes of data at the
414 * @param off offset of data in the overall value
415 * @param size number of bytes in data available
416 * @return MHD_YES to continue iterating,
417 * MHD_NO to abort the iteration
420 post_iterator (void *cls,
421 enum MHD_ValueKind kind,
423 const char *filename,
424 const char *content_type,
425 const char *transfer_encoding,
426 const char *data, uint64_t off, size_t size)
428 struct Request *request = cls;
430 if (0 == strcmp ("domain", key))
432 if (size + off >= sizeof(request->domain_name))
433 size = sizeof (request->domain_name) - off - 1;
434 memcpy (&request->domain_name[off],
437 request->domain_name[size+off] = '\0';
440 if (0 == strcmp ("pkey", key))
442 if (size + off >= sizeof(request->public_key))
443 size = sizeof (request->public_key) - off - 1;
444 memcpy (&request->public_key[off],
447 request->public_key[size+off] = '\0';
450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
451 _("Unsupported form value `%s'\n"),
460 * Continuation called to notify client about result of the
464 * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
465 * GNUNET_NO if content was already there
466 * GNUNET_YES (or other positive value) on success
467 * @param emsg NULL on success, otherwise an error message
470 put_continuation (void *cls,
474 struct Request *request = cls;
479 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
480 _("Failed to create record for domain `%s': %s\n"),
481 request->domain_name,
483 request->phase = RP_FAIL;
486 request->phase = RP_SUCCESS;
492 * Test if a name mapping was found, if so, refuse. If not, initiate storing of the record.
495 * @param zone_key public key of the zone
496 * @param expire when does the corresponding block in the DHT expire (until
497 * when should we never do a DHT lookup for the same name again)?;
498 * GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
499 * or the expiration time of the block in the namestore (even if there are zero
500 * records matching the desired record type)
501 * @param name name that is being mapped (at most 255 characters long)
502 * @param rd_count number of entries in 'rd' array
503 * @param rd array of records with data to store
504 * @param signature signature of the record block, NULL if signature is unavailable (i.e.
505 * because the user queried for a particular record type only)
508 zone_to_name_cb (void *cls,
509 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
510 struct GNUNET_TIME_Absolute expire,
512 unsigned int rd_count,
513 const struct GNUNET_NAMESTORE_RecordData *rd,
514 const struct GNUNET_CRYPTO_RsaSignature *signature)
516 struct Request *request = cls;
517 struct GNUNET_NAMESTORE_RecordData r;
518 struct GNUNET_CRYPTO_ShortHashCode pub;
523 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
524 _("Found existing name `%s' for the given key\n"),
526 request->phase = RP_FAIL;
530 GNUNET_assert (GNUNET_OK ==
531 GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
532 strlen (request->public_key),
535 r.data_size = sizeof (pub);
536 r.expiration_time = UINT64_MAX;
537 r.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
538 r.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
539 request->qe = GNUNET_NAMESTORE_record_create (ns,
541 request->domain_name,
549 * Process a record that was stored in the namestore. Used to check if
550 * the requested name already exists in the namestore. If not,
551 * proceed to check if the requested key already exists.
554 * @param zone_key public key of the zone
555 * @param expire when does the corresponding block in the DHT expire (until
556 * when should we never do a DHT lookup for the same name again)?;
557 * GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
558 * or the expiration time of the block in the namestore (even if there are zero
559 * records matching the desired record type)
560 * @param name name that is being mapped (at most 255 characters long)
561 * @param rd_count number of entries in 'rd' array
562 * @param rd array of records with data to store
563 * @param signature signature of the record block, NULL if signature is unavailable (i.e.
564 * because the user queried for a particular record type only)
567 lookup_result_processor (void *cls,
568 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
569 struct GNUNET_TIME_Absolute expire,
571 unsigned int rd_count,
572 const struct GNUNET_NAMESTORE_RecordData *rd,
573 const struct GNUNET_CRYPTO_RsaSignature *signature)
575 struct Request *request = cls;
576 struct GNUNET_CRYPTO_ShortHashCode pub;
579 GNUNET_assert (GNUNET_OK ==
580 GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
581 strlen (request->public_key),
585 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
586 _("Found %u existing records for domain `%s'\n"),
588 request->domain_name);
589 request->phase = RP_FAIL;
593 request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
602 * Main MHD callback for handling requests.
605 * @param connection MHD connection handle
606 * @param url the requested url
607 * @param method the HTTP method used ("GET", "PUT", etc.)
608 * @param version the HTTP version string (i.e. "HTTP/1.1")
609 * @param upload_data the data being uploaded (excluding HEADERS,
610 * for a POST that fits into memory and that is encoded
611 * with a supported encoding, the POST data will NOT be
612 * given in upload_data and is instead available as
613 * part of MHD_get_connection_values; very large POST
614 * data *will* be made available incrementally in
616 * @param upload_data_size set initially to the size of the
617 * upload_data provided; the method must update this
618 * value to the number of bytes NOT processed;
619 * @param ptr pointer to location where we store the 'struct Request'
620 * @return MHD_YES if the connection was handled successfully,
621 * MHD_NO if the socket must be closed due to a serious
622 * error while handling the request
625 create_response (void *cls,
626 struct MHD_Connection *connection,
630 const char *upload_data,
631 size_t *upload_data_size,
634 struct MHD_Response *response;
635 struct Request *request;
637 struct GNUNET_CRYPTO_ShortHashCode pub;
639 if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
640 (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
642 if (0 == strcmp (url, FCFS_ZONEINFO_URL))
643 ret = serve_zoneinfo_page (connection);
645 ret = serve_main_page (connection);
647 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
648 _("Failed to create page for `%s'\n"),
652 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
657 request = GNUNET_malloc (sizeof (struct Request));
659 request->pp = MHD_create_post_processor (connection, 1024,
660 &post_iterator, request);
661 if (NULL == request->pp)
663 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
664 _("Failed to setup post processor for `%s'\n"),
666 return MHD_NO; /* internal error */
670 if (NULL != request->pp)
672 /* evaluate POST data */
673 MHD_post_process (request->pp,
676 if (0 != *upload_data_size)
678 *upload_data_size = 0;
681 /* done with POST data, serve response */
682 MHD_destroy_post_processor (request->pp);
686 GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
687 strlen (request->public_key),
691 return fill_s_reply ("Failed to parse given public key",
692 request, connection);
694 switch (request->phase)
697 if (NULL != strchr (request->domain_name, (int) '.'))
699 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
700 _("Domain name must not contain `.'\n"));
701 request->phase = RP_FAIL;
702 return fill_s_reply ("Domain name must not contain `.', sorry.",
703 request, connection);
705 if (NULL != strchr (request->domain_name, (int) '+'))
707 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
708 _("Domain name must not contain `+'\n"));
709 request->phase = RP_FAIL;
710 return fill_s_reply ("Domain name must not contain `+', sorry.",
711 request, connection);
713 request->phase = RP_LOOKUP;
714 request->qe = GNUNET_NAMESTORE_lookup_record (ns,
716 request->domain_name,
717 GNUNET_NAMESTORE_TYPE_PKEY,
718 &lookup_result_processor,
726 return fill_s_reply ("Request failed, sorry.",
727 request, connection);
729 return fill_s_reply ("Success.",
730 request, connection);
735 return MHD_YES; /* will have a reply later... */
737 /* unsupported HTTP method */
738 response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
739 (void *) METHOD_ERROR,
740 MHD_RESPMEM_PERSISTENT);
741 ret = MHD_queue_response (connection,
742 MHD_HTTP_METHOD_NOT_ACCEPTABLE,
744 MHD_destroy_response (response);
750 * Callback called upon completion of a request.
751 * Decrements session reference counter.
753 * @param cls not used
754 * @param connection connection that completed
755 * @param con_cls session handle
756 * @param toe status code
759 request_completed_callback (void *cls,
760 struct MHD_Connection *connection,
762 enum MHD_RequestTerminationCode toe)
764 struct Request *request = *con_cls;
768 if (NULL != request->pp)
769 MHD_destroy_post_processor (request->pp);
770 if (NULL != request->qe)
771 GNUNET_NAMESTORE_cancel (request->qe);
772 GNUNET_free (request);
776 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
780 * Schedule tasks to run MHD server.
788 struct GNUNET_NETWORK_FDSet *wrs;
789 struct GNUNET_NETWORK_FDSet *wws;
790 struct GNUNET_NETWORK_FDSet *wes;
793 UNSIGNED_MHD_LONG_LONG timeout;
794 struct GNUNET_TIME_Relative tv;
799 wrs = GNUNET_NETWORK_fdset_create ();
800 wes = GNUNET_NETWORK_fdset_create ();
801 wws = GNUNET_NETWORK_fdset_create ();
803 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
804 haveto = MHD_get_timeout (httpd, &timeout);
805 if (haveto == MHD_YES)
806 tv.rel_value = (uint64_t) timeout;
808 tv = GNUNET_TIME_UNIT_FOREVER_REL;
809 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
810 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
811 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
813 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
816 GNUNET_NETWORK_fdset_destroy (wrs);
817 GNUNET_NETWORK_fdset_destroy (wws);
818 GNUNET_NETWORK_fdset_destroy (wes);
823 * Task run whenever HTTP server operations are pending.
826 * @param tc scheduler context
830 const struct GNUNET_SCHEDULER_TaskContext *tc)
832 httpd_task = GNUNET_SCHEDULER_NO_TASK;
839 * Task run on shutdown. Cleans up everything.
842 * @param tc scheduler context
845 do_shutdown (void *cls,
846 const struct GNUNET_SCHEDULER_TaskContext *tc)
848 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
850 GNUNET_SCHEDULER_cancel (httpd_task);
851 httpd_task = GNUNET_SCHEDULER_NO_TASK;
855 GNUNET_NAMESTORE_disconnect (ns);
860 MHD_stop_daemon (httpd);
863 if (NULL != fcfs_zone_pkey)
865 GNUNET_CRYPTO_rsa_key_free (fcfs_zone_pkey);
866 fcfs_zone_pkey = NULL;
872 * Main function that will be run.
875 * @param args remaining command-line arguments
876 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
877 * @param cfg configuration
880 run (void *cls, char *const *args, const char *cfgfile,
881 const struct GNUNET_CONFIGURATION_Handle *cfg)
884 unsigned long long port;
885 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
888 GNUNET_CONFIGURATION_get_value_number (cfg,
893 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
894 "fcfsd", "HTTPPORT");
898 GNUNET_CONFIGURATION_get_value_filename (cfg,
903 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
907 fcfs_zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
908 GNUNET_free (keyfile);
909 if (NULL == fcfs_zone_pkey)
911 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
912 _("Failed to read or create private zone key\n"));
915 GNUNET_CRYPTO_rsa_key_get_public (fcfs_zone_pkey,
917 GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &fcfsd_zone);
918 ns = GNUNET_NAMESTORE_connect (cfg);
921 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
922 _("Failed to connect to namestore\n"));
925 httpd = MHD_start_daemon (MHD_USE_DEBUG,
928 &create_response, NULL,
929 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
930 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
931 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
932 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024),
933 MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
937 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
938 _("Failed to start HTTP server\n"));
939 GNUNET_NAMESTORE_disconnect (ns);
944 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
950 * The main function for the fcfs daemon.
952 * @param argc number of arguments from the command line
953 * @param argv command line arguments
954 * @return 0 ok, 1 on error
957 main (int argc, char *const *argv)
959 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
960 GNUNET_GETOPT_OPTION_END
965 if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
968 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
971 GNUNET_PROGRAM_run (argc, argv, "fcfsd",
972 _("GNUnet GNS first come first serve registration service"),
974 &run, NULL)) ? 0 : 1;
975 GNUNET_free ((void*) argv);
979 /* end of gnunet-gns-fcfsd.c */