aboutsummaryrefslogtreecommitdiff
path: root/src/gns/gnunet-gns-fcfsd.c
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2012-03-03 22:04:41 +0000
committerChristian Grothoff <christian@grothoff.org>2012-03-03 22:04:41 +0000
commit99fc1398c292e6e3069f7743a1d8d8f382f4c879 (patch)
tree80e6c455c21c9e4e3f908a46a0c3249e53efea3b /src/gns/gnunet-gns-fcfsd.c
parent2ed8a19a0e8da985fa170906f2ea9026a2c69afa (diff)
downloadgnunet-99fc1398c292e6e3069f7743a1d8d8f382f4c879.tar.gz
gnunet-99fc1398c292e6e3069f7743a1d8d8f382f4c879.zip
-starting with a simple domain registration WWW service
Diffstat (limited to 'src/gns/gnunet-gns-fcfsd.c')
-rw-r--r--src/gns/gnunet-gns-fcfsd.c742
1 files changed, 742 insertions, 0 deletions
diff --git a/src/gns/gnunet-gns-fcfsd.c b/src/gns/gnunet-gns-fcfsd.c
new file mode 100644
index 000000000..e7ce0161b
--- /dev/null
+++ b/src/gns/gnunet-gns-fcfsd.c
@@ -0,0 +1,742 @@
1/*
2 This file is part of GNUnet.
3 (C) 2012 Christian Grothoff (and other contributing authors)
4
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.
9
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.
14
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.
19*/
20/**
21 * @file gnunet-gns-fcfsd.c
22 * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23 * @author Christian Grothoff
24 *
25 * TODO:
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 */
30#include "platform.h"
31#include <gnunet_util_lib.h>
32#include <microhttpd.h>
33#include <gnunet_namestore_service.h>
34
35/**
36 * Invalid method page.
37 */
38#define METHOD_ERROR "<html><head><title>Illegal request</title></head><body>Go away.</body></html>"
39
40/**
41 * Front page. (/)
42 */
43#define MAIN_PAGE "<html><head><title>Welcome</title></head><body><form action=\"/S\" method=\"post\">What is your desired domain name? <input type=\"text\" name=\"domain\" /> <p> What is your public key? <input type=\"text\" name=\"pkey\" /> <input type=\"submit\" value=\"Next\" /></body></html>"
44
45/**
46 * Second page (/S)
47 */
48#define SUBMIT_PAGE "<html><head><title>%s</title></head><body>%s</body></html>"
49
50/**
51 * Mime type for HTML pages.
52 */
53#define MIME_HTML "text/html"
54
55/**
56 * Name of our cookie.
57 */
58#define COOKIE_NAME "gns-fcfs"
59
60
61enum Phase
62 {
63 RP_START = 0,
64 RP_LOOKUP,
65 RP_PUT,
66 RP_SUCCESS,
67 RP_FAIL
68 };
69
70
71/**
72 * Data kept per request.
73 */
74struct Request
75{
76
77 /**
78 * Associated session.
79 */
80 struct Session *session;
81
82 /**
83 * Post processor handling form data (IF this is
84 * a POST request).
85 */
86 struct MHD_PostProcessor *pp;
87
88 /**
89 * URL to serve in response to this POST (if this request
90 * was a 'POST')
91 */
92 const char *post_url;
93
94 /**
95 * Active request with the namestore.
96 */
97 struct GNUNET_NAMESTORE_QueueEntry *qe;
98
99 /**
100 * Current processing phase.
101 */
102 enum Phase phase;
103
104 /**
105 * Domain name submitted via form.
106 */
107 char domain_name[64];
108
109 /**
110 * Public key submitted via form.
111 */
112 char public_key[1024];
113
114};
115
116
117/**
118 * MHD deamon reference.
119 */
120static struct MHD_Daemon *httpd;
121
122/**
123 * Main HTTP task.
124 */
125static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
126
127/**
128 * Handle to the namestore.
129 */
130static struct GNUNET_NAMESTORE_Handle *ns;
131
132/**
133 * Hash of the public key of the fcfsd zone.
134 */
135static GNUNET_HashCode fcfsd_zone;
136
137/**
138 * Private key for the fcfsd zone.
139 */
140static struct GNUNET_CRYPTO_RsaPrivateKey *fcfs_zone_pkey;
141
142
143/**
144 * Handler that returns a simple static HTTP page.
145 *
146 * @param connection connection to use
147 * @return MHD_YES on success
148 */
149static int
150serve_main_page (struct MHD_Connection *connection)
151{
152 int ret;
153 struct MHD_Response *response;
154
155 /* return static form */
156 response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
157 (void *) MAIN_PAGE,
158 MHD_RESPMEM_PERSISTENT);
159 MHD_add_response_header (response,
160 MHD_HTTP_HEADER_CONTENT_ENCODING,
161 MIME_HTML);
162 ret = MHD_queue_response (connection,
163 MHD_HTTP_OK,
164 response);
165 MHD_destroy_response (response);
166 return ret;
167}
168
169
170/**
171 * Send the 'SUBMIT_PAGE'.
172 *
173 * @param info information string to send to the user
174 * @param request request information
175 * @param connection connection to use
176 */
177static int
178fill_s_reply (const char *info,
179 struct Request *request,
180 struct MHD_Connection *connection)
181{
182 int ret;
183 char *reply;
184 struct MHD_Response *response;
185
186 GNUNET_asprintf (&reply,
187 SUBMIT_PAGE,
188 info,
189 info);
190 /* return static form */
191 response = MHD_create_response_from_buffer (strlen (reply),
192 (void *) reply,
193 MHD_RESPMEM_MUST_FREE);
194 MHD_add_response_header (response,
195 MHD_HTTP_HEADER_CONTENT_ENCODING,
196 MIME_HTML);
197 ret = MHD_queue_response (connection,
198 MHD_HTTP_OK,
199 response);
200 MHD_destroy_response (response);
201 return ret;
202}
203
204
205/**
206 * Iterator over key-value pairs where the value
207 * maybe made available in increments and/or may
208 * not be zero-terminated. Used for processing
209 * POST data.
210 *
211 * @param cls user-specified closure
212 * @param kind type of the value
213 * @param key 0-terminated key for the value
214 * @param filename name of the uploaded file, NULL if not known
215 * @param content_type mime-type of the data, NULL if not known
216 * @param transfer_encoding encoding of the data, NULL if not known
217 * @param data pointer to size bytes of data at the
218 * specified offset
219 * @param off offset of data in the overall value
220 * @param size number of bytes in data available
221 * @return MHD_YES to continue iterating,
222 * MHD_NO to abort the iteration
223 */
224static int
225post_iterator (void *cls,
226 enum MHD_ValueKind kind,
227 const char *key,
228 const char *filename,
229 const char *content_type,
230 const char *transfer_encoding,
231 const char *data, uint64_t off, size_t size)
232{
233 struct Request *request = cls;
234
235 if (0 == strcmp ("domain", key))
236 {
237 if (size + off > sizeof(request->domain_name))
238 size = sizeof (request->domain_name) - off;
239 memcpy (&request->domain_name[off],
240 data,
241 size);
242 if (size + off < sizeof (request->domain_name))
243 request->domain_name[size+off] = '\0';
244 return MHD_YES;
245 }
246 if (0 == strcmp ("pkey", key))
247 {
248 if (size + off > sizeof(request->public_key))
249 size = sizeof (request->public_key) - off;
250 memcpy (&request->public_key[off],
251 data,
252 size);
253 if (size + off < sizeof (request->public_key))
254 request->public_key[size+off] = '\0';
255 return MHD_YES;
256 }
257 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
258 "Unsupported form value `%s'\n", key);
259 return MHD_YES;
260}
261
262
263/**
264 * Task run whenever HTTP server operations are pending.
265 *
266 * @param cls unused
267 * @param tc scheduler context
268 */
269static void
270do_httpd (void *cls,
271 const struct GNUNET_SCHEDULER_TaskContext *tc);
272
273
274/**
275 * Schedule task to run MHD server now.
276 */
277static void
278run_httpd_now ()
279{
280 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
281 {
282 GNUNET_SCHEDULER_cancel (httpd_task);
283 httpd_task = GNUNET_SCHEDULER_NO_TASK;
284 }
285 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
286}
287
288
289/**
290 * Continuation called to notify client about result of the
291 * operation.
292 *
293 * @param cls closure
294 * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
295 * GNUNET_NO if content was already there
296 * GNUNET_YES (or other positive value) on success
297 * @param emsg NULL on success, otherwise an error message
298 */
299static void
300put_continuation (void *cls,
301 int32_t success,
302 const char *emsg)
303{
304 struct Request *request = cls;
305
306 request->qe = NULL;
307 if (0 >= success)
308 {
309 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
310 _("Failed to create record for domain `%s': %s\n"),
311 request->domain_name,
312 emsg);
313 request->phase = RP_FAIL;
314 }
315 else
316 request->phase = RP_SUCCESS;
317 run_httpd_now ();
318}
319
320
321/**
322 * Process a record that was stored in the namestore.
323 *
324 * @param cls closure
325 * @param zone_key public key of the zone
326 * @param expire when does the corresponding block in the DHT expire (until
327 * when should we never do a DHT lookup for the same name again)?;
328 * GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
329 * or the expiration time of the block in the namestore (even if there are zero
330 * records matching the desired record type)
331 * @param name name that is being mapped (at most 255 characters long)
332 * @param rd_count number of entries in 'rd' array
333 * @param rd array of records with data to store
334 * @param signature signature of the record block, NULL if signature is unavailable (i.e.
335 * because the user queried for a particular record type only)
336 */
337static void
338lookup_result_processor (void *cls,
339 const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
340 struct GNUNET_TIME_Absolute expire,
341 const char *name,
342 unsigned int rd_len,
343 const struct GNUNET_NAMESTORE_RecordData *rd,
344 const struct GNUNET_CRYPTO_RsaSignature *signature)
345{
346 struct Request *request = cls;
347 struct GNUNET_NAMESTORE_RecordData r;
348
349 request->qe = NULL;
350 if (0 != rd_len)
351 {
352 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
353 _("Found %u existing records for domain `%s'\n"),
354 rd_len,
355 request->domain_name);
356 request->phase = RP_FAIL;
357 run_httpd_now ();
358 return;
359 }
360 r.data = "binary public key";
361 r.expiration = GNUNET_TIME_UNIT_FOREVER_ABS;
362 r.data_size = sizeof ("binary public key");
363 r.record_type = htonl (GNUNET_GNS_TYPE_PKEY);
364 r.flags = htonl (GNUNET_NAMESTORE_RF_AUTHORITY);
365 request->qe = GNUNET_NAMESTORE_record_create (ns,
366 fcfs_zone_pkey,
367 request->domain_name,
368 &r,
369 &put_continuation,
370 request);
371}
372
373
374/**
375 * Main MHD callback for handling requests.
376 *
377 *
378 * @param cls argument given together with the function
379 * pointer when the handler was registered with MHD
380 * @param url the requested url
381 * @param method the HTTP method used ("GET", "PUT", etc.)
382 * @param version the HTTP version string (i.e. "HTTP/1.1")
383 * @param upload_data the data being uploaded (excluding HEADERS,
384 * for a POST that fits into memory and that is encoded
385 * with a supported encoding, the POST data will NOT be
386 * given in upload_data and is instead available as
387 * part of MHD_get_connection_values; very large POST
388 * data *will* be made available incrementally in
389 * upload_data)
390 * @param upload_data_size set initially to the size of the
391 * upload_data provided; the method must update this
392 * value to the number of bytes NOT processed;
393 * @param con_cls pointer that the callback can set to some
394 * address and that will be preserved by MHD for future
395 * calls for this request; since the access handler may
396 * be called many times (i.e., for a PUT/POST operation
397 * with plenty of upload data) this allows the application
398 * to easily associate some request-specific state.
399 * If necessary, this state can be cleaned up in the
400 * global "MHD_RequestCompleted" callback (which
401 * can be set with the MHD_OPTION_NOTIFY_COMPLETED).
402 * Initially, <tt>*con_cls</tt> will be NULL.
403 * @return MHS_YES if the connection was handled successfully,
404 * MHS_NO if the socket must be closed due to a serios
405 * error while handling the request
406 */
407static int
408create_response (void *cls,
409 struct MHD_Connection *connection,
410 const char *url,
411 const char *method,
412 const char *version,
413 const char *upload_data,
414 size_t *upload_data_size,
415 void **ptr)
416{
417 struct MHD_Response *response;
418 struct Request *request;
419 int ret;
420
421 if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
422 (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
423 {
424 ret = serve_main_page (connection);
425 if (ret != MHD_YES)
426 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
427 _("Failed to create page for `%s'\n"),
428 url);
429 return ret;
430 }
431 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
432 {
433 request = *ptr;
434 if (NULL == request)
435 {
436 request = GNUNET_malloc (sizeof (struct Request));
437 *ptr = request;
438 request->pp = MHD_create_post_processor (connection, 1024,
439 &post_iterator, request);
440 if (NULL == request->pp)
441 {
442 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
443 _("Failed to setup post processor for `%s'\n"),
444 url);
445 return MHD_NO; /* internal error */
446 }
447 return MHD_YES;
448 }
449 if (NULL != request->pp)
450 {
451 /* evaluate POST data */
452 MHD_post_process (request->pp,
453 upload_data,
454 *upload_data_size);
455 if (0 != *upload_data_size)
456 {
457 *upload_data_size = 0;
458 return MHD_YES;
459 }
460 /* done with POST data, serve response */
461 MHD_destroy_post_processor (request->pp);
462 request->pp = NULL;
463 }
464 if (strlen (request->public_key) != 2)
465 {
466 /* parse error */
467 return fill_s_reply ("Failed to parse given public key",
468 request, connection);
469 }
470 fprintf (stderr, "Now in phase %d\n", request->phase);
471 switch (request->phase)
472 {
473 case RP_START:
474 request->phase = RP_LOOKUP;
475 request->qe = GNUNET_NAMESTORE_lookup_record (ns,
476 &fcfsd_zone,
477 request->domain_name,
478 GNUNET_GNS_TYPE_PKEY,
479 &lookup_result_processor,
480 request);
481 break;
482 case RP_LOOKUP:
483 break;
484 case RP_PUT:
485 break;
486 case RP_FAIL:
487 return fill_s_reply ("Request failed, sorry.",
488 request, connection);
489 case RP_SUCCESS:
490 return fill_s_reply ("Success.",
491 request, connection);
492 default:
493 GNUNET_break (0);
494 return MHD_NO;
495 }
496 return MHD_YES; /* will have a reply later... */
497 }
498 /* unsupported HTTP method */
499 response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
500 (void *) METHOD_ERROR,
501 MHD_RESPMEM_PERSISTENT);
502 ret = MHD_queue_response (connection,
503 MHD_HTTP_METHOD_NOT_ACCEPTABLE,
504 response);
505 MHD_destroy_response (response);
506 return ret;
507}
508
509
510/**
511 * Callback called upon completion of a request.
512 * Decrements session reference counter.
513 *
514 * @param cls not used
515 * @param connection connection that completed
516 * @param con_cls session handle
517 * @param toe status code
518 */
519static void
520request_completed_callback (void *cls,
521 struct MHD_Connection *connection,
522 void **con_cls,
523 enum MHD_RequestTerminationCode toe)
524{
525 struct Request *request = *con_cls;
526
527 if (NULL == request)
528 return;
529 if (NULL != request->pp)
530 MHD_destroy_post_processor (request->pp);
531 if (NULL != request->qe)
532 GNUNET_NAMESTORE_cancel (request->qe);
533 GNUNET_free (request);
534}
535
536
537/**
538 * Schedule tasks to run MHD server.
539 */
540static void
541run_httpd ()
542{
543 fd_set rs;
544 fd_set ws;
545 fd_set es;
546 struct GNUNET_NETWORK_FDSet *wrs;
547 struct GNUNET_NETWORK_FDSet *wws;
548 struct GNUNET_NETWORK_FDSet *wes;
549 int max;
550 int haveto;
551 unsigned MHD_LONG_LONG timeout;
552 struct GNUNET_TIME_Relative tv;
553
554 FD_ZERO (&rs);
555 FD_ZERO (&ws);
556 FD_ZERO (&es);
557 wrs = GNUNET_NETWORK_fdset_create ();
558 wes = GNUNET_NETWORK_fdset_create ();
559 wws = GNUNET_NETWORK_fdset_create ();
560 max = -1;
561 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
562 haveto = MHD_get_timeout (httpd, &timeout);
563 if (haveto == MHD_YES)
564 tv.rel_value = (uint64_t) timeout;
565 else
566 tv = GNUNET_TIME_UNIT_FOREVER_REL;
567 GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
568 GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
569 GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
570 httpd_task =
571 GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
572 GNUNET_SCHEDULER_NO_TASK, tv, wrs, wws,
573 &do_httpd, NULL);
574 GNUNET_NETWORK_fdset_destroy (wrs);
575 GNUNET_NETWORK_fdset_destroy (wws);
576 GNUNET_NETWORK_fdset_destroy (wes);
577}
578
579
580/**
581 * Task run whenever HTTP server operations are pending.
582 *
583 * @param cls unused
584 * @param tc scheduler context
585 */
586static void
587do_httpd (void *cls,
588 const struct GNUNET_SCHEDULER_TaskContext *tc)
589{
590 httpd_task = GNUNET_SCHEDULER_NO_TASK;
591 MHD_run (httpd);
592 run_httpd ();
593}
594
595
596/**
597 * Task run on shutdown. Cleans up everything.
598 *
599 * @param cls unused
600 * @param tc scheduler context
601 */
602static void
603do_shutdown (void *cls,
604 const struct GNUNET_SCHEDULER_TaskContext *tc)
605{
606 if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
607 {
608 GNUNET_SCHEDULER_cancel (httpd_task);
609 httpd_task = GNUNET_SCHEDULER_NO_TASK;
610 }
611 if (NULL != ns)
612 {
613 GNUNET_NAMESTORE_disconnect (ns, GNUNET_NO);
614 ns = NULL;
615 }
616 if (NULL != httpd)
617 {
618 MHD_stop_daemon (httpd);
619 httpd = NULL;
620 }
621 if (NULL != fcfs_zone_pkey)
622 {
623 GNUNET_CRYPTO_rsa_key_free (fcfs_zone_pkey);
624 fcfs_zone_pkey = NULL;
625 }
626}
627
628
629/**
630 * Main function that will be run.
631 *
632 * @param cls closure
633 * @param args remaining command-line arguments
634 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
635 * @param cfg configuration
636 */
637static void
638run (void *cls, char *const *args, const char *cfgfile,
639 const struct GNUNET_CONFIGURATION_Handle *cfg)
640{
641 char *keyfile;
642 unsigned long long port;
643 struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
644
645 if (GNUNET_OK !=
646 GNUNET_CONFIGURATION_get_value_number (cfg,
647 "fcfsd",
648 "HTTPPORT",
649 &port))
650 {
651 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
652 _("Option `%s' not specified in configuration section `%s'\n"),
653 "HTTPPORT",
654 "fcfsd");
655 return;
656 }
657 if (GNUNET_OK !=
658 GNUNET_CONFIGURATION_get_value_filename (cfg,
659 "fcfsd",
660 "ZONEKEY",
661 &keyfile))
662 {
663 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
664 _("Option `%s' not specified in configuration section `%s'\n"),
665 "ZONEKEY",
666 "fcfsd");
667 return;
668 }
669 fcfs_zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
670 GNUNET_free (keyfile);
671 if (NULL == fcfs_zone_pkey)
672 {
673 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
674 _("Failed to read or create private zone key\n"));
675 return;
676 }
677 GNUNET_CRYPTO_rsa_key_get_public (fcfs_zone_pkey,
678 &pub);
679 GNUNET_CRYPTO_hash (&pub, sizeof (pub), &fcfsd_zone);
680 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
681 _("Managing `%s' as FCFS zone on port %llu\n"),
682 GNUNET_h2s_full (&fcfsd_zone),
683 port);
684 ns = GNUNET_NAMESTORE_connect (cfg);
685 if (NULL == ns)
686 {
687 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
688 _("Failed to connect to namestore\n"));
689 return;
690 }
691 httpd = MHD_start_daemon (MHD_USE_DEBUG,
692 (uint16_t) port,
693 NULL, NULL,
694 &create_response, NULL,
695 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
696 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
697 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
698 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024),
699 MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
700 MHD_OPTION_END);
701 if (NULL == httpd)
702 {
703 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
704 _("Failed to start HTTP server\n"));
705 GNUNET_NAMESTORE_disconnect (ns, GNUNET_NO);
706 ns = NULL;
707 return;
708 }
709 run_httpd ();
710 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
711 &do_shutdown, NULL);
712}
713
714
715/**
716 * The main function for the fcfs daemon.
717 *
718 * @param argc number of arguments from the command line
719 * @param argv command line arguments
720 * @return 0 ok, 1 on error
721 */
722int
723main (int argc, char *const *argv)
724{
725 static const struct GNUNET_GETOPT_CommandLineOption options[] = {
726 GNUNET_GETOPT_OPTION_END
727 };
728
729 int ret;
730
731 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
732 ret =
733 (GNUNET_OK ==
734 GNUNET_PROGRAM_run (argc, argv, "fcfsd",
735 _("GNUnet GNS first come first serve registration service"),
736 options,
737 &run, NULL)) ? 0 : 1;
738
739 return ret;
740}
741
742/* end of gnunet-gns-fcfsd.c */