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