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.c1167
1 files changed, 0 insertions, 1167 deletions
diff --git a/src/namestore/gnunet-namestore-fcfsd.c b/src/namestore/gnunet-namestore-fcfsd.c
deleted file mode 100644
index 7e96ffa43..000000000
--- a/src/namestore/gnunet-namestore-fcfsd.c
+++ /dev/null
@@ -1,1167 +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 * 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_IDENTITY_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
435/**
436 * A name/key pair has been successfully registered, or maybe not.
437 *
438 * @param cls the connection
439 * @param status result of the operation
440 * @param emsg error message if any
441 */
442static void
443register_done_cb (void *cls,
444 int32_t status,
445 const char *emsg)
446{
447 struct RequestData *rd = cls;
448
449 MHD_resume_connection (rd->c);
450 rd->searching = NULL;
451
452 if ((GNUNET_SYSERR == status) || (GNUNET_NO == status))
453 {
454 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
455 _ ("Failed to create record for `%s': %s\n"),
456 rd->register_name,
457 emsg);
458 rd->body = make_json ("error", "true",
459 "message", emsg,
460 NULL);
461 rd->body_length = strlen (rd->body);
462 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
463 }
464 else
465 {
466 rd->body = make_json ("error", "false",
467 "message", _ ("no errors"),
468 NULL);
469 rd->body_length = strlen (rd->body);
470 rd->code = MHD_HTTP_OK;
471 }
472
473 run_httpd_now ();
474}
475
476
477/**
478 * Attempt to register the requested name.
479 *
480 * @param cls the connection
481 * @param key the zone key
482 * @param label name of the record
483 * @param count number of records found
484 * @param d records
485 */
486static void
487register_do_cb (void *cls,
488 const struct GNUNET_IDENTITY_PrivateKey *key,
489 const char *label,
490 unsigned int count,
491 const struct GNUNET_GNSRECORD_Data *d)
492{
493 (void) key;
494 (void) d;
495
496 struct RequestData *rd = cls;
497
498 rd->searching = NULL;
499
500 if (0 != count)
501 {
502 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
503 _ ("The requested key `%s' exists as `%s'\n"),
504 rd->register_key,
505 label);
506
507 MHD_resume_connection (rd->c);
508 rd->searching = NULL;
509 rd->body = make_json ("error", "true",
510 "message", _ ("key exists"),
511 NULL);
512 rd->body_length = strlen (rd->body);
513 rd->code = MHD_HTTP_FORBIDDEN;
514 run_httpd_now ();
515 return;
516 }
517
518 struct GNUNET_GNSRECORD_Data gd;
519 char *gdraw = NULL;
520
521 if (GNUNET_OK != GNUNET_GNSRECORD_data_from_identity (&(rd->key),
522 &gdraw,
523 &(gd.data_size),
524 &(gd.record_type)))
525 {
526 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
527 _ ("Error creating record data\n"));
528 MHD_resume_connection (rd->c);
529 rd->searching = NULL;
530 rd->body = make_json ("error", "true",
531 "message", _ ("unable to store record"),
532 NULL);
533 rd->body_length = strlen (rd->body);
534 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
535 run_httpd_now ();
536 return;
537 }
538
539 gd.data = gdraw;
540 gd.expiration_time = record_exp.rel_value_us;
541 gd.flags = GNUNET_GNSRECORD_RF_RELATIVE_EXPIRATION;
542
543 rd->searching = GNUNET_NAMESTORE_records_store (namestore,
544 zone_key,
545 rd->register_name,
546 1,
547 &gd,
548 &register_done_cb,
549 rd);
550
551 GNUNET_free (gdraw);
552}
553
554
555/**
556 * An error occurred while iterating the namestore.
557 *
558 * @param cls the connection
559 */
560static void
561iterate_error_cb (void *cls)
562{
563 struct RequestData *rd = cls;
564
565 MHD_resume_connection (rd->c);
566 rd->iterating = NULL;
567 rd->body = make_json ("error", "true",
568 "message", _ ("unable to scan namestore"),
569 NULL);
570 rd->body_length = strlen (rd->body);
571 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
572 run_httpd_now ();
573}
574
575
576/**
577 * A block was received from the namestore.
578 *
579 * @param cls the connection
580 * @param key the zone key
581 * @param label the records' label
582 * @param count number of records found
583 * @param d the found records
584 */
585static void
586iterate_do_cb (void *cls,
587 const struct GNUNET_IDENTITY_PrivateKey *key,
588 const char *label,
589 unsigned int count,
590 const struct GNUNET_GNSRECORD_Data *d)
591{
592 (void) key;
593 (void) label;
594 (void) d;
595
596 struct RequestData *rd = cls;
597
598 if (0 == strcmp (label, rd->register_name))
599 {
600 GNUNET_break (0 != count);
601 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
602 _ ("Requested name `%s' exists with `%u' records\n"),
603 rd->register_name,
604 count);
605
606 MHD_resume_connection (rd->c);
607 rd->body = make_json ("error", "true",
608 "message", _ ("name exists\n"),
609 NULL);
610 rd->body_length = strlen (rd->body);
611 rd->code = MHD_HTTP_FORBIDDEN;
612 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
613 run_httpd_now ();
614 return;
615 }
616
617 GNUNET_NAMESTORE_zone_iterator_next (rd->iterating, 1);
618}
619
620
621/**
622 * All entries in the namestore have been iterated over.
623 *
624 * @param cls the connection
625 */
626static void
627iterate_done_cb (void *cls)
628{
629 struct RequestData *rd = cls;
630
631 rd->iterating = NULL;
632
633 /* See if the key was not registered already */
634 rd->searching = GNUNET_NAMESTORE_zone_to_name (namestore,
635 zone_key,
636 &(rd->key),
637 &register_error_cb,
638 rd,
639 &register_do_cb,
640 rd);
641}
642
643
644/**
645 * Generate a response containing JSON and send it to the client.
646 *
647 * @param c the connection
648 * @param body the response body
649 * @param length the body length in bytes
650 * @param code the response code
651 * @return MHD_NO on error
652 */
653static MHD_RESULT
654serve_json (struct MHD_Connection *c,
655 char *body,
656 size_t length,
657 int code)
658{
659 struct MHD_Response *response =
660 MHD_create_response_from_buffer (length,
661 body,
662 MHD_RESPMEM_PERSISTENT);
663 MHD_RESULT r = MHD_queue_response (c, code, response);
664 MHD_destroy_response (response);
665 return r;
666}
667
668
669/**
670 * Send a response back to a connected client.
671 *
672 * @param cls unused
673 * @param connection the connection with the client
674 * @param url the requested address
675 * @param method the HTTP method used
676 * @param version the protocol version (including the "HTTP/" part)
677 * @param upload_data data sent with a POST request
678 * @param upload_data_size length in bytes of the POST data
679 * @param ptr used to pass data between request handling phases
680 * @return MHD_NO on error
681 */
682static MHD_RESULT
683create_response (void *cls,
684 struct MHD_Connection *connection,
685 const char *url,
686 const char *method,
687 const char *version,
688 const char *upload_data,
689 size_t *upload_data_size,
690 void **ptr)
691{
692 (void) cls;
693 (void) version;
694
695 struct RequestData *rd = *ptr;
696
697 if (0 == strcmp (method, MHD_HTTP_METHOD_GET))
698 {
699 /* Handle a previously suspended request */
700 if (NULL != rd)
701 {
702 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
703 }
704
705 if (0 == strcmp ("/", url))
706 {
707 return MHD_queue_response (connection,
708 MHD_HTTP_OK,
709 main_page->response);
710 }
711
712 if (0 == strcmp ("/search", url))
713 {
714 const char *name = MHD_lookup_connection_value (connection,
715 MHD_GET_ARGUMENT_KIND,
716 "name");
717 if (NULL == name)
718 {
719 return MHD_queue_response (connection,
720 MHD_HTTP_BAD_REQUEST,
721 forbidden_page->response);
722 }
723
724 MHD_suspend_connection (connection);
725 rd = GNUNET_new (struct RequestData);
726 rd->c = connection;
727 rd->searching = GNUNET_NAMESTORE_records_lookup (namestore,
728 zone_key,
729 name,
730 &search_error_cb,
731 rd,
732 &search_done_cb,
733 rd);
734 *ptr = rd;
735 return MHD_YES;
736 }
737
738 return MHD_queue_response (connection,
739 MHD_HTTP_NOT_FOUND,
740 notfound_page->response);
741 }
742
743 if (0 == strcmp (method, MHD_HTTP_METHOD_HEAD))
744 {
745 /* We take a shortcut here by always serving the main page: starting a
746 namestore lookup, allocating the necessary resources, waiting for the
747 lookup to complete and then discard everything just because it was a HEAD
748 and thus only the headers are significative, is an unnecessary waste of
749 resources. The handling of this method could be smarter, for example by
750 sending a proper content type header based on the endpoint, but this is
751 not a service in which HEAD requests are significant, so there's no need
752 to spend too much time here. */
753 return MHD_queue_response (connection,
754 MHD_HTTP_OK,
755 main_page->response);
756 }
757
758 if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
759 {
760 if (0 == strcmp ("/register", url))
761 {
762 /* Handle a previously suspended request */
763 if ((NULL != rd) && (NULL != rd->body))
764 {
765 return serve_json (rd->c, rd->body, rd->body_length, rd->code);
766 }
767
768 if (NULL == rd)
769 {
770 rd = GNUNET_new (struct RequestData);
771 rd->c = connection;
772 rd->body = NULL;
773 rd->ptr = NULL;
774 *ptr = rd;
775 }
776
777 json_t *json = NULL;
778 enum GNUNET_JSON_PostResult result =
779 GNUNET_JSON_post_parser (32 * 1024,
780 connection,
781 &(rd->ptr),
782 upload_data,
783 upload_data_size,
784 &json);
785
786 switch (result)
787 {
788 case GNUNET_JSON_PR_CONTINUE:
789 /* Keep processing POST data */
790 return MHD_YES;
791 case GNUNET_JSON_PR_OUT_OF_MEMORY:
792 case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
793 rd->body = make_json ("error", "true",
794 "message", _ ("unable to process submitted data"),
795 NULL);
796 rd->body_length = strlen (rd->body);
797#ifdef MHD_HTTP_CONTENT_TOO_LARGE
798 rd->code = MHD_HTTP_CONTENT_TOO_LARGE;
799#else
800 rd->code = MHD_HTTP_PAYLOAD_TOO_LARGE;
801#endif
802 return MHD_YES;
803 case GNUNET_JSON_PR_JSON_INVALID:
804 rd->body = make_json ("error", "true",
805 "message", _ ("the submitted data is invalid"),
806 NULL);
807 rd->body_length = strlen (rd->body);
808 rd->code = MHD_HTTP_BAD_REQUEST;
809 return MHD_YES;
810 default:
811 break;
812 }
813
814 /* POST data has been read in its entirety */
815
816 const char *name = json_string_value (json_object_get (json, "name"));
817 const char *key = json_string_value (json_object_get (json, "key"));
818 if ((NULL == name) || (NULL == key) || (0 == strlen (name)) || (0 ==
819 strlen (
820 key)))
821 {
822 json_decref (json);
823 rd->body = make_json ("error", "true",
824 "message", _ ("invalid parameters"),
825 NULL);
826 rd->body_length = strlen (rd->body);
827 rd->code = MHD_HTTP_BAD_REQUEST;
828 return MHD_YES;
829 }
830
831 rd->register_name = strdup (name);
832 rd->register_key = strdup (key);
833
834 json_decref (json);
835 GNUNET_JSON_post_parser_cleanup (rd->ptr);
836
837 if ((NULL != strchr (rd->register_name, '.')) ||
838 (NULL != strchr (rd->register_name, '+')))
839 {
840 rd->body = make_json ("error", "true",
841 "message", _ ("invalid name"),
842 NULL);
843 rd->body_length = strlen (rd->body);
844 rd->code = MHD_HTTP_BAD_REQUEST;
845 return MHD_YES;
846 }
847
848 if (GNUNET_OK != GNUNET_IDENTITY_public_key_from_string (rd->register_key,
849 &(rd->key)))
850 {
851 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
852 _ ("Unable to parse key %s\n"),
853 rd->register_key);
854
855 rd->body = make_json ("error", "true",
856 "message", _ ("unable to parse key"),
857 NULL);
858 rd->body_length = strlen (rd->body);
859 rd->code = MHD_HTTP_INTERNAL_SERVER_ERROR;
860 return MHD_YES;
861 }
862
863 MHD_suspend_connection (connection);
864 /* See if the requested name is free */
865 rd->iterating =
866 GNUNET_NAMESTORE_zone_iteration_start (namestore,
867 zone_key,
868 &iterate_error_cb,
869 rd,
870 &iterate_do_cb,
871 rd,
872 &iterate_done_cb,
873 rd);
874 return MHD_YES;
875 }
876
877 return MHD_queue_response (connection,
878 MHD_HTTP_FORBIDDEN,
879 forbidden_page->response);
880 }
881
882 return MHD_queue_response (connection,
883 MHD_HTTP_NOT_IMPLEMENTED,
884 forbidden_page->response);
885}
886
887
888/**
889 * Called when a request is completed.
890 *
891 * @param cls unused
892 * @param connection the connection
893 * @param ptr connection-specific data
894 * @param status status code
895 */
896static void
897completed_cb (void *cls,
898 struct MHD_Connection *connection,
899 void **ptr,
900 enum MHD_RequestTerminationCode status)
901{
902 (void) cls;
903 (void) connection;
904 (void) status;
905
906 struct RequestData *rd = *ptr;
907
908 if (NULL == rd)
909 {
910 return;
911 }
912
913 if (NULL == rd->body)
914 {
915 GNUNET_free (rd->body);
916 }
917
918 if (NULL != rd->searching)
919 {
920 GNUNET_NAMESTORE_cancel (rd->searching);
921 }
922
923 if (NULL != rd->register_name)
924 {
925 GNUNET_free (rd->register_name);
926 }
927
928 if (NULL != rd->register_key)
929 {
930 GNUNET_free (rd->register_key);
931 }
932
933 if (NULL != rd->iterating)
934 {
935 GNUNET_NAMESTORE_zone_iteration_stop (rd->iterating);
936 }
937
938 GNUNET_free (rd);
939}
940
941
942/**
943 * Called for each ego provided by the identity service.
944 *
945 * @param cls closure
946 * @param ego the ego
947 * @param ctx application-provided data for the ego
948 * @param name the ego name
949 */
950static void
951identity_cb (void *cls,
952 struct GNUNET_IDENTITY_Ego *ego,
953 void **ctx,
954 const char *name)
955{
956 (void) cls;
957 (void) ctx;
958
959 if ((NULL == name) || (0 != strcmp (name, zone)))
960 {
961 return;
962 }
963
964 if (NULL == ego)
965 {
966 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
967 _ ("No ego configured for `fcfsd` subsystem\n"));
968 GNUNET_SCHEDULER_shutdown ();
969 return;
970 }
971
972 zone_key = GNUNET_IDENTITY_ego_get_private_key (ego);
973
974 int flags = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
975 do
976 {
977 httpd = MHD_start_daemon (flags,
978 (uint16_t) port,
979 NULL, NULL,
980 &create_response, NULL,
981 MHD_OPTION_CONNECTION_LIMIT, 128,
982 MHD_OPTION_PER_IP_CONNECTION_LIMIT, 1,
983 MHD_OPTION_CONNECTION_TIMEOUT, 4 * 1024,
984 MHD_OPTION_NOTIFY_COMPLETED, &completed_cb, NULL,
985 MHD_OPTION_END);
986 flags = MHD_USE_DEBUG;
987 } while (NULL == httpd && flags != MHD_USE_DEBUG);
988
989 if (NULL == httpd)
990 {
991 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
992 _ ("Failed to start HTTP server\n"));
993 GNUNET_SCHEDULER_shutdown ();
994 return;
995 }
996
997 run_httpd ();
998}
999
1000
1001/**
1002 * Open a file on disk and generate a response object for it.
1003 *
1004 * @param name name of the file to open
1005 * @param basedir directory where the file is located
1006 * @return NULL on error
1007 */
1008static struct StaticPage *
1009open_static_page (const char *name, const char *basedir)
1010{
1011 char *fullname = NULL;
1012 GNUNET_asprintf (&fullname, "%s/fcfsd-%s", basedir, name);
1013
1014 struct GNUNET_DISK_FileHandle *f =
1015 GNUNET_DISK_file_open (fullname,
1016 GNUNET_DISK_OPEN_READ,
1017 GNUNET_DISK_PERM_NONE);
1018 GNUNET_free (fullname);
1019
1020 if (NULL == f)
1021 {
1022 return NULL;
1023 }
1024
1025 off_t size = 0;
1026 if (GNUNET_SYSERR == GNUNET_DISK_file_handle_size (f, &size))
1027 {
1028 GNUNET_DISK_file_close (f);
1029 return NULL;
1030 }
1031
1032 struct MHD_Response *response =
1033 MHD_create_response_from_fd64 (size,
1034 f->fd);
1035
1036 if (NULL == response)
1037 {
1038 GNUNET_DISK_file_close (f);
1039 return NULL;
1040 }
1041
1042 struct StaticPage *page = GNUNET_new (struct StaticPage);
1043 page->handle = f;
1044 page->size = (uint64_t) size;
1045 page->response = response;
1046 return page;
1047}
1048
1049
1050/**
1051 * Called after the service is up.
1052 *
1053 * @param cls closure
1054 * @param args remaining command line arguments
1055 * @param cfgfile name of the configuration file
1056 * @param cfg the service configuration
1057 */
1058static void
1059run_service (void *cls,
1060 char *const *args,
1061 const char *cfgfile,
1062 const struct GNUNET_CONFIGURATION_Handle *cfg)
1063{
1064 (void) cls;
1065 (void) args;
1066 (void) cfgfile;
1067
1068 GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1069
1070 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_time (cfg,
1071 "fcfsd",
1072 "RELATIVE_RECORD_EXPIRATION",
1073 &record_exp))
1074 {
1075 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1076 _("No expiration specified for records.\n"));
1077 GNUNET_SCHEDULER_shutdown ();
1078 return;
1079 }
1080
1081 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_number (cfg,
1082 "fcfsd",
1083 "HTTPPORT",
1084 &port))
1085 {
1086 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1087 _ ("No port specified, using default value\n"));
1088 }
1089
1090 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1091
1092 namestore = GNUNET_NAMESTORE_connect (cfg);
1093 if (NULL == namestore)
1094 {
1095 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1096 _ ("Failed to connect to namestore\n"));
1097 GNUNET_SCHEDULER_shutdown ();
1098 return;
1099 }
1100
1101 identity = GNUNET_IDENTITY_connect (cfg, &identity_cb, NULL);
1102 if (NULL == identity)
1103 {
1104 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1105 _ ("Failed to connect to identity\n"));
1106 GNUNET_SCHEDULER_shutdown ();
1107 return;
1108 }
1109
1110 char *basedir = NULL;
1111 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg,
1112 "fcfsd",
1113 "HTMLDIR",
1114 &basedir))
1115 {
1116 basedir = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
1117 }
1118
1119 main_page = open_static_page ("index.html", basedir);
1120 notfound_page = open_static_page ("notfound.html", basedir);
1121 forbidden_page = open_static_page ("forbidden.html", basedir);
1122
1123 GNUNET_free (basedir);
1124
1125 if ((NULL == main_page) || (NULL == notfound_page) || (NULL ==
1126 forbidden_page) )
1127 {
1128 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1129 _ ("Unable to set up the daemon\n"));
1130 GNUNET_SCHEDULER_shutdown ();
1131 return;
1132 }
1133}
1134
1135
1136/**
1137 * The main function of the fcfs daemon.
1138 *
1139 * @param argc number of arguments from the command line
1140 * @parsm argv the command line arguments
1141 * @return 0 successful exit, a different value otherwise
1142 */
1143int
1144main (int argc, char *const *argv)
1145{
1146 struct GNUNET_GETOPT_CommandLineOption options[] = {
1147 GNUNET_GETOPT_option_mandatory
1148 (GNUNET_GETOPT_option_string ('z',
1149 "zone",
1150 "EGO",
1151 gettext_noop (
1152 "name of the zone managed by FCFSD"),
1153 &zone)),
1154 GNUNET_GETOPT_OPTION_END
1155 };
1156
1157 return ((GNUNET_OK == GNUNET_PROGRAM_run (argc,
1158 argv,
1159 "gnunet-namestore-fcfsd",
1160 _ (
1161 "GNU Name System First-Come-First-Served name registration service"),
1162 options,
1163 &run_service,
1164 NULL)) ?
1165 0 :
1166 1);
1167}