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