aboutsummaryrefslogtreecommitdiff
path: root/src/namestore/gnunet-namestore-fcfsd.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/namestore/gnunet-namestore-fcfsd.c')
-rw-r--r--src/namestore/gnunet-namestore-fcfsd.c1147
1 files changed, 0 insertions, 1147 deletions
diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c
deleted file mode 100644
index 7ec9db156..000000000
--- a/src/namestore/gnunet-namestore-fcfsd.c
+++ /dev/null
@@ -1,1147 +0,0 @@
1/*
2 This file is part of GNUnet.
3 Copyright (C) 2012-2021 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your 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 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20
21/**
22 * @file gnunet-namestore-fcfsd.c
23 * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
24 * @author Christian Grothoff
25 */
26
27#include "platform.h"
28#include <microhttpd.h>
29#include "gnunet_util_lib.h"
30#include "gnunet_identity_service.h"
31#include "gnunet_gnsrecord_lib.h"
32#include "gnunet_namestore_service.h"
33#include "gnunet_mhd_compat.h"
34#include "gnunet_json_lib.h"
35
36/**
37 * Structure representing a static page.
38 * "Static" means that the server does not process the page before sending it
39 * to the client. Clients can still process the received data, for example
40 * because there are scripting elements within.
41 */
42struct StaticPage
43{
44 /**
45 * Handle to file on disk.
46 */
47 struct GNUNET_DISK_FileHandle *handle;
48
49 /**
50 * Size in bytes of the file.
51 */
52 uint64_t size;
53
54 /**
55 * Cached response object to send to clients.
56 */
57 struct MHD_Response *response;
58};
59
60/**
61 * Structure containing some request-specific data.
62 */
63struct RequestData
64{
65 /**
66 * The connection this request was sent in.
67 */
68 struct MHD_Connection *c;
69
70 /**
71 * Body of the response object.
72 */
73 char *body;
74
75 /**
76 * Length in bytes of the body.
77 */
78 size_t body_length;
79
80 /**
81 * Response code.
82 */
83 int code;
84
85 /**
86 * Task started to search for an entry in the namestore.
87 */
88 struct GNUNET_NAMESTORE_QueueEntry *searching;
89
90 /**
91 * Task started to iterate over the namestore.
92 */
93 struct GNUNET_NAMESTORE_ZoneIterator *iterating;
94
95 /**
96 * Pointer used while processing POST data.
97 */
98 void *ptr;
99
100 /**
101 * Name requested to be registered.
102 */
103 char *register_name;
104
105 /**
106 * Key (encoded as a string) to be associated with the requested name.
107 */
108 char *register_key;
109
110 /**
111 * Key to be associated with the requested name.
112 */
113 struct GNUNET_IDENTITY_PublicKey key;
114};
115
116/**
117 * Name of the zone being managed.
118 */
119static char *zone = NULL;
120
121/**
122 * The port the daemon is listening to for HTTP requests.
123 */
124static unsigned long long port = 18080;
125
126/**
127 * Connection with the namestore service.
128 */
129static struct GNUNET_NAMESTORE_Handle *namestore = NULL;
130
131/**
132 * Connection with the identity service.
133 */
134static struct GNUNET_IDENTITY_Handle *identity = NULL;
135
136/**
137 * Private key of the zone.
138 */
139static const struct GNUNET_IDENTITY_PrivateKey *zone_key = NULL;
140
141/**
142 * The HTTP daemon.
143 */
144static struct MHD_Daemon *httpd = NULL;
145
146/**
147 * Task executing the HTTP daemon.
148 */
149static struct GNUNET_SCHEDULER_Task *httpd_task = NULL;
150
151/**
152 * The main page, a.k.a. "index.html"
153 */
154static struct StaticPage *main_page = NULL;
155
156/**
157 * Page indicating the requested resource could not be found.
158 */
159static struct StaticPage *notfound_page = NULL;
160
161/**
162 * Page indicating the requested resource could not be accessed, and other
163 * errors.
164 */
165static struct StaticPage *forbidden_page = NULL;
166
167/**
168 * Task ran at shutdown to clean up everything.
169 *
170 * @param cls unused
171 */
172static void
173do_shutdown (void *cls)
174{
175 /* We cheat a bit here: the file descriptor is implicitly closed by MHD, so
176 calling `GNUNET_DISK_file_close' would generate a spurious warning message
177 in the log. Since that function does nothing but close the descriptor and
178 free the allocated memory, After destroying the response all that's left to
179 do is call `GNUNET_free'. */
180 if (NULL != main_page)
181 {
182 MHD_destroy_response (main_page->response);
183 GNUNET_free (main_page->handle);
184 GNUNET_free (main_page);
185 }
186 if (NULL != notfound_page)
187 {
188 MHD_destroy_response (notfound_page->response);
189 GNUNET_free (notfound_page->handle);
190 GNUNET_free (notfound_page);
191 }
192 if (NULL != forbidden_page)
193 {
194 MHD_destroy_response (forbidden_page->response);
195 GNUNET_free (forbidden_page->handle);
196 GNUNET_free (forbidden_page);
197 }
198
199 if (NULL != namestore)
200 {
201 GNUNET_NAMESTORE_disconnect (namestore);
202 }
203
204 if (NULL != identity)
205 {
206 GNUNET_IDENTITY_disconnect (identity);
207 }
208
209 if (NULL != httpd_task)
210 {
211 GNUNET_SCHEDULER_cancel (httpd_task);
212 }
213 if (NULL != httpd)
214 {
215 MHD_stop_daemon (httpd);
216 }
217}
218
219
220/**
221 * Called when the HTTP server has some pending operations.
222 *
223 * @param cls unused
224 */
225static void
226do_httpd (void *cls);
227
228/**
229 * Schedule a task to run MHD.
230 */
231static void
232run_httpd (void)
233{
234 fd_set rs;
235 fd_set ws;
236 fd_set es;
237
238 struct GNUNET_NETWORK_FDSet *grs = GNUNET_NETWORK_fdset_create ();
239 struct GNUNET_NETWORK_FDSet *gws = GNUNET_NETWORK_fdset_create ();
240 struct GNUNET_NETWORK_FDSet *ges = GNUNET_NETWORK_fdset_create ();
241
242 FD_ZERO (&rs);
243 FD_ZERO (&ws);
244 FD_ZERO (&es);
245
246 int max = -1;
247 GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
248
249 unsigned MHD_LONG_LONG timeout = 0;
250 struct GNUNET_TIME_Relative gtime = GNUNET_TIME_UNIT_FOREVER_REL;
251 if (MHD_YES == MHD_get_timeout (httpd, &timeout))
252 {
253 gtime = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
254 timeout);
255 }
256
257 GNUNET_NETWORK_fdset_copy_native (grs, &rs, max + 1);
258 GNUNET_NETWORK_fdset_copy_native (gws, &ws, max + 1);
259 GNUNET_NETWORK_fdset_copy_native (ges, &es, max + 1);
260
261 httpd_task = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
262 gtime,
263 grs,
264 gws,
265 &do_httpd,
266 NULL);
267 GNUNET_NETWORK_fdset_destroy (grs);
268 GNUNET_NETWORK_fdset_destroy (gws);
269 GNUNET_NETWORK_fdset_destroy (ges);
270}
271
272
273/**
274 * Called when the HTTP server has some pending operations.
275 *
276 * @param cls unused
277 */
278static void
279do_httpd (void *cls)
280{
281 httpd_task = NULL;
282 MHD_run (httpd);
283 run_httpd ();
284}
285
286
287static void
288run_httpd_now (void)
289{
290 if (NULL != httpd_task)
291 {
292 GNUNET_SCHEDULER_cancel (httpd_task);
293 httpd_task = NULL;
294 }
295 httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
296}
297
298
299/**
300 * Generate a JSON object.
301 *
302 * @param key the key for the first element
303 * @param value the value for the first element
304 * @param ... key-value pairs of the object, terminated by NULL
305 * @return a JSON string (allocated)
306 */
307static char *
308make_json (const char *key, const char *value, ...)
309{
310 va_list args;
311 va_start (args, value);
312
313 json_t *obj = NULL;
314
315 obj = json_object ();
316 if ((NULL == key) || (NULL == value))
317 {
318 va_end (args);
319 return json_dumps (obj, JSON_COMPACT);
320 }
321
322 json_object_set (obj, key, json_string (value));
323
324 char *k = va_arg (args, char *);
325 if (NULL == k)
326 {
327 va_end (args);
328 return json_dumps (obj, JSON_COMPACT);
329 }
330 char *v = va_arg (args, char *);
331 if (NULL == v)
332 {
333 va_end (args);
334 return json_dumps (obj, JSON_COMPACT);
335 }
336
337 while (NULL != k && NULL != v)
338 {
339 json_object_set (obj, k, json_string (v));
340 k = va_arg (args, char *);
341 if (NULL != k)
342 {
343 v = va_arg (args, char *);
344 }
345 }
346
347 va_end (args);
348
349 char *json = json_dumps (obj, JSON_COMPACT);
350 json_decref (obj);
351
352 return json;
353}
354
355
356/**
357 * The namestore search task failed.
358 *
359 * @param cls the request data
360 */
361static void
362search_error_cb (void *cls)
363{
364 struct RequestData *rd = cls;
365 MHD_resume_connection (rd->c);
366 rd->searching = NULL;
367 rd->body = make_json ("error", "true",
368 "message", _ ("can not search the namestore"),
369 NULL);
370 rd->body_length = strlen (rd->body);
371 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
372 run_httpd_now ();
373}
374
375
376/**
377 * The lookup terminated with some results.
378 *
379 * @param cls closure
380 * @param zone the private key of the zone
381 * @param label the result label
382 * @param count number of records found
383 * @param d records found
384 */
385static void
386search_done_cb (void *cls,
387 const struct GNUNET_IDENTITY_PrivateKey *zone,
388 const char *label,
389 unsigned int count,
390 const struct GNUNET_GNSRECORD_Data *d)
391{
392 (void) zone;
393 (void) d;
394
395 struct RequestData *rd = cls;
396 MHD_resume_connection (rd->c);
397
398 rd->searching = NULL;
399 rd->body = make_json ("error", "false",
400 "free", (0 == count) ? "true" : "false",
401 NULL);
402 rd->body_length = strlen (rd->body);
403 rd->code = MHD_HTTP_OK;
404
405 run_httpd_now ();
406}
407
408
409/**
410 * An error occurred while registering a name.
411 *
412 * @param cls the connection
413 */
414static void
415register_error_cb (void *cls)
416{
417 struct RequestData *rd = cls;
418
419 MHD_resume_connection (rd->c);
420 rd->searching = NULL;
421 rd->body = make_json ("error", "true",
422 "message", _ ("unable to scan namestore"),
423 NULL);
424 rd->body_length = strlen (rd->body);
425 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
426 run_httpd_now ();
427}
428
429
430/**
431 * A name/key pair has been successfully registered, or maybe not.
432 *
433 * @param cls the connection
434 * @param status result of the operation
435 * @param emsg error message if any
436 */
437static void
438register_done_cb (void *cls,
439 int32_t status,
440 const char *emsg)
441{
442 struct RequestData *rd = cls;
443
444 MHD_resume_connection (rd->c);
445 rd->searching = NULL;
446
447 if ((GNUNET_SYSERR == status) || (GNUNET_NO == status))
448 {
449 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
450 _ ("Failed to create record for `%s': %s\n"),
451 rd->register_name,
452 emsg);
453 rd->body = make_json ("error", "true",
454 "message", emsg,
455 NULL);
456 rd->body_length = strlen (rd->body);
457 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
458 }
459 else
460 {
461 rd->body = make_json ("error", "false",
462 "message", _ ("no errors"),
463 NULL);
464 rd->body_length = strlen (rd->body);
465 rd->code = MHD_HTTP_OK;
466 }
467
468 run_httpd_now ();
469}
470
471
472/**
473 * Attempt to register the requested name.
474 *
475 * @param cls the connection
476 * @param key the zone key
477 * @param label name of the record
478 * @param count number of records found
479 * @param d records
480 */
481static void
482register_do_cb (void *cls,
483 const struct GNUNET_IDENTITY_PrivateKey *key,
484 const char *label,
485 unsigned int count,
486 const struct GNUNET_GNSRECORD_Data *d)
487{
488 (void) key;
489 (void) d;
490
491 struct RequestData *rd = cls;
492
493 rd->searching = NULL;
494
495 if (0 != count)
496 {
497 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
498 _ ("The requested key `%s' exists as `%s'\n"),
499 rd->register_key,
500 label);
501
502 MHD_resume_connection (rd->c);
503 rd->searching = NULL;
504 rd->body = make_json ("error", "true",
505 "message", _ ("key exists"),
506 NULL);
507 rd->body_length = strlen (rd->body);
508 rd->code = MHD_HTTP_FORBIDDEN;
509 run_httpd_now ();
510 return;
511 }
512
513 struct GNUNET_GNSRECORD_Data gd;
514 char *gdraw = NULL;
515
516 if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key),
517 &gdraw,
518 &(gd.data_size),
519 &(gd.record_type)))
520 {
521 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
522 _ ("Error creating record data\n"));
523 MHD_resume_connection (rd->c);
524 rd->searching = NULL;
525 rd->body = make_json ("error", "true",
526 "message", _ ("unable to store record"),
527 NULL);
528 rd->body_length = strlen (rd->body);
529 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
530 run_httpd_now ();
531 return;
532 }
533
534 gd.data = gdraw;
535 gd.expiration_time = UINT64_MAX;
536 gd.flags = GNUNET_GNSRECORD_RF_NONE;
537
538 rd->searching = GNUNET_NAMESTORE_records_store (namestore,
539 zone_key,
540 rd->register_name,
541 1,
542 &gd,
543 &register_done_cb,
544 rd);
545
546 GNUNET_free (gdraw);
547}
548
549
550/**
551 * An error occurred while iterating the namestore.
552 *
553 * @param cls the connection
554 */
555static void
556iterate_error_cb (void *cls)
557{
558 struct RequestData *rd = cls;
559
560 MHD_resume_connection (rd->c);
561 rd->iterating = NULL;
562 rd->body = make_json ("error", "true",
563 "message", _ ("unable to scan namestore"),
564 NULL);
565 rd->body_length = strlen (rd->body);
566 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
567 run_httpd_now ();
568}
569
570
571/**
572 * A block was received from the namestore.
573 *
574 * @param cls the connection
575 * @param key the zone key
576 * @param label the records' label
577 * @param count number of records found
578 * @param d the found records
579 */
580static void
581iterate_do_cb (void *cls,
582 const struct GNUNET_IDENTITY_PrivateKey *key,
583 const char *label,
584 unsigned int count,
585 const struct GNUNET_GNSRECORD_Data *d)
586{
587 (void) key;
588 (void) label;
589 (void) d;
590
591 struct RequestData *rd = cls;
592
593 if (0 == strcmp (label, rd->register_name))
594 {
595 GNUNET_break (0 != count);
596 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
597 _ ("Requested name `%s' exists with `%u' records\n"),
598 rd->register_name,
599 count);
600
601 MHD_resume_connection (rd->c);
602 rd->body = make_json ("error", "true",
603 "message", _ ("name exists\n"),
604 NULL);
605 rd->body_length = strlen (rd->body);
606 rd->code = MHD_HTTP_FORBIDDEN;
607 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
608 run_httpd_now ();
609 return;
610 }
611
612 GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1);
613}
614
615
616/**
617 * All entries in the namestore have been iterated over.
618 *
619 * @param cls the connection
620 */
621static void
622iterate_done_cb (void *cls)
623{
624 struct RequestData *rd = cls;
625
626 rd->iterating = NULL;
627
628 /* See if the key was not registered already */
629 rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore,
630 zone_key,
631 &(rd->key),
632 &register_error_cb,
633 rd,
634 &register_do_cb,
635 rd);
636}
637
638
639/**
640 * Generate a response containing JSON and send it to the client.
641 *
642 * @param c the connection
643 * @param body the response body
644 * @param length the body length in bytes
645 * @param code the response code
646 * @return MHD_NO on error
647 */
648static MHD_RESULT
649serve_json (struct MHD_Connection *c,
650 char *body,
651 size_t length,
652 int code)
653{
654 struct MHD_Response *response =
655 MHD_create_response_from_buffer (length,
656 body,
657 MHD_RESPMEM_PERSISTENT);
658 MHD_RESULT r = MHD_queue_response (c, code, response);
659 MHD_destroy_response (response);
660 return r;
661}
662
663
664/**
665 * Send a response back to a connected client.
666 *
667 * @param cls unused
668 * @param connection the connection with the client
669 * @param url the requested address
670 * @param method the HTTP method used
671 * @param version the protocol version (including the "HTTP/" part)
672 * @param upload_data data sent with a POST request
673 * @param upload_data_size length in bytes of the POST data
674 * @param ptr used to pass data between request handling phases
675 * @return MHD_NO on error
676 */
677static MHD_RESULT
678create_response (void *cls,
679 struct MHD_Connection *connection,
680 const char *url,
681 const char *method,
682 const char *version,
683 const char *upload_data,
684 size_t *upload_data_size,
685 void **ptr)
686{
687 (void) cls;
688 (void) version;
689
690 struct RequestData *rd = *ptr;
691
692 if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
693 {
694 /* Handle a previously suspended request */
695 if (NULL != rd)
696 {
697 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
698 }
699
700 if (0 == strcmp ("/", url))
701 {
702 return MHD_queue_response (connection,
703 MHD_HTTP_OK,
704 main_page->response);
705 }
706
707 if (0 == strcmp ("/search", url))
708 {
709 const char *name = MHD_lookup_connection_value (connection,
710 MHD_GET_ARGUMENT_KIND,
711 "name");
712 if (NULL == name)
713 {
714 return MHD_queue_response (connection,
715 MHD_HTTP_BAD_REQUEST,
716 forbidden_page->response);
717 }
718
719 MHD_suspend_connection (connection);
720 rd = GNUNET_new (struct RequestData);
721 rd->c = connection;
722 rd->searching = GNUNET_NAMESTORE_records_lookup (namestore,
723 zone_key,
724 name,
725 &search_error_cb,
726 rd,
727 &search_done_cb,
728 rd);
729 *ptr = rd;
730 return MHD_YES;
731 }
732
733 return MHD_queue_response (connection,
734 MHD_HTTP_NOT_FOUND,
735 notfound_page->response);
736 }
737
738 if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD))
739 {
740 /* We take a shortcut here by always serving the main page: starting a
741 namestore lookup, allocating the necessary resources, waiting for the
742 lookup to complete and then discard everything just because it was a HEAD
743 and thus only the headers are significative, is an unnecessary waste of
744 resources. The handling of this method could be smarter, for example by
745 sending a proper content type header based on the endpoint, but this is
746 not a service in which HEAD requests are significant, so there's no need
747 to spend too much time here. */
748 return MHD_queue_response (connection,
749 MHD_HTTP_OK,
750 main_page->response);
751 }
752
753 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
754 {
755 if (0 == strcmp ("/register", url))
756 {
757 /* Handle a previously suspended request */
758 if ((NULL != rd) && (NULL != rd->body))
759 {
760 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
761 }
762
763 if (NULL == rd)
764 {
765 rd = GNUNET_new (struct RequestData);
766 rd->c = connection;
767 rd->body = NULL;
768 rd->ptr = NULL;
769 *ptr = rd;
770 }
771
772 json_t *json = NULL;
773 enum GNUNET_JSON_PostResult result =
774 GNUNET_JSON_post_parser (32 * 1024,
775 connection,
776 &(rd->ptr),
777 upload_data,
778 upload_data_size,
779 &json);
780
781 switch (result)
782 {
783 case GNUNET_JSON_PR_CONTINUE:
784 /* Keep processing POST data */
785 return MHD_YES;
786 case GNUNET_JSON_PR_OUT_OF_MEMORY:
787 case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
788 rd->body = make_json ("error", "true",
789 "message", _ ("unable to process submitted data"),
790 NULL);
791 rd->body_length = strlen (rd->body);
792 rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE;
793 return MHD_YES;
794 case GNUNET_JSON_PR_JSON_INVALID:
795 rd->body = make_json ("error", "true",
796 "message", _ ("the submitted data is invalid"),
797 NULL);
798 rd->body_length = strlen (rd->body);
799 rd->code = MHD_HTTP_BAD_REQUEST;
800 return MHD_YES;
801 default:
802 break;
803 }
804
805 /* POST data has been read in its entirety */
806
807 const char *name = json_string_value (json_object_get (json, "name"));
808 const char *key = json_string_value (json_object_get (json, "key"));
809 if ((NULL == name) || (NULL == key) || (0 == strlen (name)) || (0 ==
810 strlen (
811 key)))
812 {
813 json_decref (json);
814 rd->body = make_json ("error", "true",
815 "message", _ ("invalid parameters"),
816 NULL);
817 rd->body_length = strlen (rd->body);
818 rd->code = MHD_HTTP_BAD_REQUEST;
819 return MHD_YES;
820 }
821
822 rd->register_name = strdup (name);
823 rd->register_key = strdup (key);
824
825 json_decref (json);
826 GNUNET_JSON_post_parser_cleanup (rd->ptr);
827
828 if ((NULL != strchr (rd->register_name, '.')) ||
829 (NULL != strchr (rd->register_name, '+')))
830 {
831 rd->body = make_json ("error", "true",
832 "message", _ ("invalid name"),
833 NULL);
834 rd->body_length = strlen (rd->body);
835 rd->code = MHD_HTTP_BAD_REQUEST;
836 return MHD_YES;
837 }
838
839 if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (rd->register_key,
840 &(rd->key)))
841 {
842 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
843 _ ("Unable to parse key %s\n"),
844 rd->register_key);
845
846 rd->body = make_json ("error", "true",
847 "message", _ ("unable to parse key"),
848 NULL);
849 rd->body_length = strlen (rd->body);
850 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
851 return MHD_YES;
852 }
853
854 MHD_suspend_connection (connection);
855 /* See if the requested name is free */
856 rd->iterating =
857 GNUNET_NAMESTORE_zone_iteration_start (namestore,
858 zone_key,
859 &iterate_error_cb,
860 rd,
861 &iterate_do_cb,
862 rd,
863 &iterate_done_cb,
864 rd);
865 return MHD_YES;
866 }
867
868 return MHD_queue_response (connection,
869 MHD_HTTP_FORBIDDEN,
870 forbidden_page->response);
871 }
872
873 return MHD_queue_response (connection,
874 MHD_HTTP_NOT_IMPLEMENTED,
875 forbidden_page->response);
876}
877
878
879/**
880 * Called when a request is completed.
881 *
882 * @param cls unused
883 * @param connection the connection
884 * @param ptr connection-specific data
885 * @param status status code
886 */
887static void
888completed_cb (void *cls,
889 struct MHD_Connection *connection,
890 void **ptr,
891 enum MHD_RequestTerminationCode status)
892{
893 (void) cls;
894 (void) connection;
895 (void) status;
896
897 struct RequestData *rd = *ptr;
898
899 if (NULL == rd)
900 {
901 return;
902 }
903
904 if (NULL == rd->body)
905 {
906 GNUNET_free (rd->body);
907 }
908
909 if (NULL != rd->searching)
910 {
911 GNUNET_NAMESTORE_cancel (rd->searching);
912 }
913
914 if (NULL != rd->register_name)
915 {
916 GNUNET_free (rd->register_name);
917 }
918
919 if (NULL != rd->register_key)
920 {
921 GNUNET_free (rd->register_key);
922 }
923
924 if (NULL != rd->iterating)
925 {
926 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
927 }
928
929 GNUNET_free (rd);
930}
931
932
933/**
934 * Called for each ego provided by the identity service.
935 *
936 * @param cls closure
937 * @param ego the ego
938 * @param ctx application-provided data for the ego
939 * @param name the ego name
940 */
941static void
942identity_cb (void *cls,
943 struct GNUNET_IDENTITY_Ego *ego,
944 void **ctx,
945 const char *name)
946{
947 (void) cls;
948 (void) ctx;
949
950 if ((NULL == name) || (0 != strcmp (name, zone)))
951 {
952 return;
953 }
954
955 if (NULL == ego)
956 {
957 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
958 _ ("No ego configured for `fcfsd` subsystem\n"));
959 GNUNET_SCHEDULER_shutdown ();
960 return;
961 }
962
963 zone_key = GNUNET_IDENTITY_ego_get_private_key (ego);
964
965 int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
966 do
967 {
968 httpd = MHD_start_daemon (flags,
969 (uint16_t) port,
970 NULL, NULL,
971 &create_response, NULL,
972 MHD_OPTION_CONNECTION_LIMIT, 128,
973 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1,
974 MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024,
975 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
976 MHD_OPTION_END);
977 flags = MHD_USE_DEBUG;
978 } while (NULL == httpd && flags != MHD_USE_DEBUG);
979
980 if (NULL == httpd)
981 {
982 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
983 _ ("Failed to start HTTP server\n"));
984 GNUNET_SCHEDULER_shutdown ();
985 return;
986 }
987
988 run_httpd ();
989}
990
991
992/**
993 * Open a file on disk and generate a response object for it.
994 *
995 * @param name name of the file to open
996 * @param basedir directory where the file is located
997 * @return NULL on error
998 */
999static struct StaticPage *
1000open_static_page (const char *name, const char *basedir)
1001{
1002 char *fullname = NULL;
1003 GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name);
1004
1005 struct GNUNET_DISK_FileHandle *f =
1006 GNUNET_DISK_file_open (fullname,
1007 GNUNET_DISK_OPEN_READ,
1008 GNUNET_DISK_PERM_NONE);
1009 GNUNET_free (fullname);
1010
1011 if (NULL == f)
1012 {
1013 return NULL;
1014 }
1015
1016 off_t size = 0;
1017 if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size))
1018 {
1019 GNUNET_DISK_file_close (f);
1020 return NULL;
1021 }
1022
1023 struct MHD_Response *response =
1024 MHD_create_response_from_fd64 (size,
1025 f->fd);
1026
1027 if (NULL == response)
1028 {
1029 GNUNET_DISK_file_close (f);
1030 return NULL;
1031 }
1032
1033 struct StaticPage *page = GNUNET_new (struct StaticPage);
1034 page->handle = f;
1035 page->size = (uint64_t) size;
1036 page->response = response;
1037 return page;
1038}
1039
1040
1041/**
1042 * Called after the service is up.
1043 *
1044 * @param cls closure
1045 * @param args remaining command line arguments
1046 * @param cfgfile name of the configuration file
1047 * @param cfg the service configuration
1048 */
1049static void
1050run_service (void *cls,
1051 char *const *args,
1052 const char *cfgfile,
1053 const struct GNUNET_CONFIGURATION_Handle *cfg)
1054{
1055 (void) cls;
1056 (void) args;
1057 (void) cfgfile;
1058
1059 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1060
1061 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1062 "fcfsd",
1063 "HTTPPORT",
1064 &port))
1065 {
1066 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1067 _ ("No port specified, using default value\n"));
1068 }
1069
1070 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1071
1072 namestore = GNUNET_NAMESTORE_connect (cfg);
1073 if (NULL == namestore)
1074 {
1075 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1076 _ ("Failed to connect to namestore\n"));
1077 GNUNET_SCHEDULER_shutdown ();
1078 return;
1079 }
1080
1081 identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1082 if (NULL == identity)
1083 {
1084 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1085 _ ("Failed to connect to identity\n"));
1086 GNUNET_SCHEDULER_shutdown ();
1087 return;
1088 }
1089
1090 char *basedir = NULL;
1091 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1092 "fcfsd",
1093 "HTMLDIR",
1094 &basedir))
1095 {
1096 basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1097 }
1098
1099 main_page = open_static_page ("index.html", basedir);
1100 notfound_page = open_static_page ("notfound.html", basedir);
1101 forbidden_page = open_static_page ("forbidden.html", basedir);
1102
1103 GNUNET_free (basedir);
1104
1105 if ((NULL == main_page) || (NULL == notfound_page) || (NULL ==
1106 forbidden_page) )
1107 {
1108 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1109 _ ("Unable to set up the daemon\n"));
1110 GNUNET_SCHEDULER_shutdown ();
1111 return;
1112 }
1113}
1114
1115
1116/**
1117 * The main function of the fcfs daemon.
1118 *
1119 * @param argc number of arguments from the command line
1120 * @parsm argv the command line arguments
1121 * @return 0 successful exit, a different value otherwise
1122 */
1123int
1124main (int argc, char *const *argv)
1125{
1126 struct GNUNET_GETOPT_CommandLineOption options[] = {
1127 GNUNET_GETOPT_option_mandatory
1128 (GNUNET_GETOPT_option_string ('z',
1129 "zone",
1130 "EGO",
1131 gettext_noop (
1132 "name of the zone managed by FCFSD"),
1133 &zone)),
1134 GNUNET_GETOPT_OPTION_END
1135 };
1136
1137 return ((GNUNET_OK == GNUNET_PROGRAM_run (argc,
1138 argv,
1139 "gnunet-namestore-fcfsd",
1140 _ (
1141 "GNU Name System First-Come-First-Served name registration service"),
1142 options,
1143 &run_service,
1144 NULL)) ?
1145 0 :
1146 1);
1147}