error handling
[oweals/gnunet.git] / src / namestore / gnunet-namestore-fcfsd.c
1 /*
2      This file is part of GNUnet.
3      Copyright (C) 2012-2014 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  * @file gnunet-namestore-fcfsd.c
22  * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - need to track active zone info requests so we can cancel them
27  *   during shutdown, right?
28  * - the code currently contains a 'race' between checking that the
29  *   domain name is available and allocating it to the new public key
30  *   (should this race be solved by namestore or by fcfsd?)
31  * - nicer error reporting to browser
32  */
33 #include "platform.h"
34 #include <microhttpd.h>
35 #include "gnunet_util_lib.h"
36 #include "gnunet_identity_service.h"
37 #include "gnunet_gnsrecord_lib.h"
38 #include "gnunet_namestore_service.h"
39
40 /**
41  * Invalid method page.
42  */
43 #define METHOD_ERROR \
44   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>Illegal request</title></head><body>Go away.</body></html>"
45
46 /**
47  * Front page. (/)
48  */
49 #define MAIN_PAGE \
50   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>GNUnet FCFS Authority Name Registration Service</title></head><body><form action=\"S\" method=\"post\">What is your desired domain name? (at most 63 lowercase characters, no dots allowed.) <input type=\"text\" name=\"domain\" /> <p> What is your public key? (Copy from gnunet-setup.) <input type=\"text\" name=\"pkey\" /> <input type=\"submit\" value=\"Next\" /><br/><a href=./Zoneinfo> List of all registered names </a></body></html>"
51
52 /**
53  * Second page (/S)
54  */
55 #define SUBMIT_PAGE \
56   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>%s</title></head><body>%s</body></html>"
57
58 /**
59  * Fcfs zoneinfo page (/Zoneinfo)
60  */
61 #define ZONEINFO_PAGE \
62   "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"><html lang=\"en\" xmlns=\"http://www.w3.org/1999/xhtml\"><html><head><title>FCFS Zoneinfo</title></head><body><h1> FCFS Zoneinfo </h1><table border=\"1\"><th>name</th><th>PKEY</th>%s</table></body></html>"
63
64 #define FCFS_ZONEINFO_URL "/Zoneinfo"
65
66 /**
67  * Mime type for HTML pages.
68  */
69 #define MIME_HTML "text/html"
70
71 /**
72  * Name of our cookie.
73  */
74 #define COOKIE_NAME "namestore-fcfsd"
75
76 #define DEFAULT_ZONEINFO_BUFSIZE 2048
77
78 /**
79  * Phases a request goes through.
80  */
81 enum Phase
82 {
83   /**
84    * Start phase (parsing POST, checking).
85    */
86   RP_START = 0,
87
88   /**
89    * Lookup to see if the domain name is taken.
90    */
91   RP_LOOKUP,
92
93   /**
94    * Storing of the record.
95    */
96   RP_PUT,
97
98   /**
99    * We're done with success.
100    */
101   RP_SUCCESS,
102
103   /**
104    * Send failure message.
105    */
106   RP_FAIL
107 };
108
109
110 /**
111  * Data kept per request.
112  */
113 struct Request
114 {
115   /**
116    * Associated session.
117    */
118   // FIXME: struct Session *session;
119
120   /**
121    * Post processor handling form data (IF this is
122    * a POST request).
123    */
124   struct MHD_PostProcessor *pp;
125
126   /**
127    * MHD Connection
128    */
129   struct MHD_Connection *con;
130   /**
131    * URL to serve in response to this POST (if this request
132    * was a 'POST')
133    */
134   const char *post_url;
135
136   /**
137    * Active request with the namestore.
138    */
139   struct GNUNET_NAMESTORE_QueueEntry *qe;
140
141   /**
142    * Active lookup iterator
143    * TODO: deprecate or fix lookup by label and use above member
144    */
145   struct GNUNET_NAMESTORE_ZoneIterator *lookup_it;
146   /**
147    * Active iteration with the namestore.
148    */
149   struct GNUNET_NAMESTORE_ZoneIterator *zi;
150
151   /**
152    * Current processing phase.
153    */
154   enum Phase phase;
155
156   /**
157    * Domain name submitted via form.
158    */
159   char domain_name[64];
160
161   /**
162    * Public key submitted via form.
163    */
164   char public_key[128];
165
166   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
167 };
168
169 /**
170  * Zoneinfo request
171  */
172 struct ZoneinfoRequest
173 {
174   /**
175    * List iterator
176    */
177   struct GNUNET_NAMESTORE_ZoneIterator *list_it;
178
179   /**
180    * Buffer
181    */
182   char*zoneinfo;
183
184   /**
185    * Buffer length
186    */
187   size_t buf_len;
188
189   /**
190    * Buffer write offset
191    */
192   size_t write_offset;
193 };
194
195 /**
196  * MHD deamon reference.
197  */
198 static struct MHD_Daemon *httpd;
199
200 /**
201  * Main HTTP task.
202  */
203 static struct GNUNET_SCHEDULER_Task *httpd_task;
204
205 /**
206  * Handle to the namestore.
207  */
208 static struct GNUNET_NAMESTORE_Handle *ns;
209
210 /**
211  * Private key for the fcfsd zone.
212  */
213 static struct GNUNET_CRYPTO_EcdsaPrivateKey fcfs_zone_pkey;
214
215 /**
216  * Connection to identity service.
217  */
218 static struct GNUNET_IDENTITY_Handle *identity;
219
220 /**
221  * Zoneinfo page we currently use.
222  */
223 static struct MHD_Response *info_page;
224
225 /**
226  * Task that runs #update_zoneinfo_page peridicially.
227  */
228 static struct GNUNET_SCHEDULER_Task *uzp_task;
229
230 /**
231  * Request for our ego.
232  */
233 static struct GNUNET_IDENTITY_Operation *id_op;
234
235 /**
236  * Port we use for the HTTP server.
237  */
238 static unsigned long long port;
239
240 /**
241  * Name of the zone we manage.
242  */
243 static char *zone;
244
245
246 /**
247  * Task run whenever HTTP server operations are pending.
248  *
249  * @param cls unused
250  */
251 static void
252 do_httpd (void *cls);
253
254
255 /**
256  * Schedule task to run MHD server now.
257  */
258 static void
259 run_httpd_now ()
260 {
261   if (NULL != httpd_task)
262   {
263     GNUNET_SCHEDULER_cancel (httpd_task);
264     httpd_task = NULL;
265   }
266   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
267 }
268
269
270 /**
271  * Create fresh version of zone information.
272  */
273 static void
274 update_zoneinfo_page (void *cls);
275
276
277 /**
278  * Function called on error in zone iteration.
279  */
280 static void
281 zone_iteration_error (void *cls)
282 {
283   struct ZoneinfoRequest *zr = cls;
284
285   zr->list_it = NULL;
286   GNUNET_free (zr->zoneinfo);
287   GNUNET_SCHEDULER_cancel (uzp_task);
288   uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
289                                        NULL);
290 }
291
292
293 /**
294  * Function called once the zone iteration is done.
295  */
296 static void
297 zone_iteration_end (void *cls)
298 {
299   struct ZoneinfoRequest *zr = cls;
300   struct MHD_Response *response;
301   char*full_page;
302
303   zr->list_it = NULL;
304
305   /* return static form */
306   GNUNET_asprintf (&full_page,
307                    ZONEINFO_PAGE,
308                    zr->zoneinfo,
309                    zr->zoneinfo);
310   response = MHD_create_response_from_buffer (strlen (full_page),
311                                               (void *) full_page,
312                                               MHD_RESPMEM_MUST_FREE);
313   MHD_add_response_header (response,
314                            MHD_HTTP_HEADER_CONTENT_TYPE,
315                            MIME_HTML);
316   MHD_destroy_response (info_page);
317   info_page = response;
318   GNUNET_free (zr->zoneinfo);
319 }
320
321
322 /**
323  * Process a record that was stored in the namestore, adding
324  * the information to the HTML.
325  *
326  * @param cls closure with the `struct ZoneinfoRequest *`
327  * @param zone_key private key of the zone; NULL on disconnect
328  * @param name label of the records; NULL on disconnect
329  * @param rd_len number of entries in @a rd array, 0 if label was deleted
330  * @param rd array of records with data to store
331  */
332 static void
333 iterate_cb (void *cls,
334             const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
335             const char *name,
336             unsigned int rd_len,
337             const struct GNUNET_GNSRECORD_Data *rd)
338 {
339   struct ZoneinfoRequest *zr = cls;
340   size_t bytes_free;
341   char*pkey;
342   char*new_buf;
343
344   (void) zone_key;
345   if (1 != rd_len)
346   {
347     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
348                                          1);
349     return;
350   }
351
352   if (GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type)
353   {
354     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
355                                          1);
356     return;
357   }
358
359   bytes_free = zr->buf_len - zr->write_offset;
360   pkey = GNUNET_GNSRECORD_value_to_string (rd->record_type,
361                                            rd->data,
362                                            rd->data_size);
363   if (NULL == pkey)
364   {
365     GNUNET_break (0);
366     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
367                                          1);
368     return;
369   }
370   if (bytes_free < (strlen (name) + strlen (pkey) + 40))
371   {
372     new_buf = GNUNET_malloc (zr->buf_len * 2);
373     GNUNET_memcpy (new_buf, zr->zoneinfo, zr->write_offset);
374     GNUNET_free (zr->zoneinfo);
375     zr->zoneinfo = new_buf;
376     zr->buf_len *= 2;
377   }
378   sprintf (zr->zoneinfo + zr->write_offset,
379            "<tr><td>%s</td><td>%s</td></tr>",
380            name,
381            pkey);
382   zr->write_offset = strlen (zr->zoneinfo);
383   GNUNET_NAMESTORE_zone_iterator_next (zr->list_it,
384                                        1);
385   GNUNET_free (pkey);
386 }
387
388
389 /**
390  * Handler that returns FCFS zoneinfo page.
391  *
392  * @param connection connection to use
393  */
394 static int
395 serve_zoneinfo_page (struct MHD_Connection *connection)
396 {
397   return MHD_queue_response (connection,
398                              MHD_HTTP_OK,
399                              info_page);
400 }
401
402
403 /**
404  * Create fresh version of zone information.
405  */
406 static void
407 update_zoneinfo_page (void *cls)
408 {
409   static struct ZoneinfoRequest zr;
410
411   (void) cls;
412   uzp_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
413                                            &update_zoneinfo_page,
414                                            NULL);
415   if (NULL != zr.list_it)
416     return;
417   zr.zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
418   zr.buf_len = DEFAULT_ZONEINFO_BUFSIZE;
419   zr.write_offset = 0;
420   zr.list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
421                                                       &fcfs_zone_pkey,
422                                                       &zone_iteration_error,
423                                                       &zr,
424                                                       &iterate_cb,
425                                                       &zr,
426                                                       &zone_iteration_end,
427                                                       &zr);
428 }
429
430
431 /**
432  * Handler that returns a simple static HTTP page.
433  *
434  * @param connection connection to use
435  * @return #MHD_YES on success
436  */
437 static int
438 serve_main_page (struct MHD_Connection *connection)
439 {
440   int ret;
441   struct MHD_Response *response;
442
443   /* return static form */
444   response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
445                                               (void *) MAIN_PAGE,
446                                               MHD_RESPMEM_PERSISTENT);
447   MHD_add_response_header (response,
448                            MHD_HTTP_HEADER_CONTENT_TYPE,
449                            MIME_HTML);
450   ret = MHD_queue_response (connection,
451                             MHD_HTTP_OK,
452                             response);
453   MHD_destroy_response (response);
454   return ret;
455 }
456
457
458 /**
459  * Send the 'SUBMIT_PAGE'.
460  *
461  * @param info information string to send to the user
462  * @param request request information
463  * @param connection connection to use
464  */
465 static int
466 fill_s_reply (const char *info,
467               struct Request *request,
468               struct MHD_Connection *connection)
469 {
470   int ret;
471   char *reply;
472   struct MHD_Response *response;
473
474   (void) request;
475   GNUNET_asprintf (&reply,
476                    SUBMIT_PAGE,
477                    info,
478                    info);
479   /* return static form */
480   response = MHD_create_response_from_buffer (strlen (reply),
481                                               (void *) reply,
482                                               MHD_RESPMEM_MUST_FREE);
483   MHD_add_response_header (response,
484                            MHD_HTTP_HEADER_CONTENT_TYPE,
485                            MIME_HTML);
486   ret = MHD_queue_response (connection,
487                             MHD_HTTP_OK,
488                             response);
489   MHD_destroy_response (response);
490   return ret;
491 }
492
493
494 /**
495  * Iterator over key-value pairs where the value
496  * maybe made available in increments and/or may
497  * not be zero-terminated.  Used for processing
498  * POST data.
499  *
500  * @param cls user-specified closure
501  * @param kind type of the value
502  * @param key 0-terminated key for the value
503  * @param filename name of the uploaded file, NULL if not known
504  * @param content_type mime-type of the data, NULL if not known
505  * @param transfer_encoding encoding of the data, NULL if not known
506  * @param data pointer to size bytes of data at the
507  *              specified offset
508  * @param off offset of data in the overall value
509  * @param size number of bytes in data available
510  * @return MHD_YES to continue iterating,
511  *         MHD_NO to abort the iteration
512  */
513 static int
514 post_iterator (void *cls,
515                enum MHD_ValueKind kind,
516                const char *key,
517                const char *filename,
518                const char *content_type,
519                const char *transfer_encoding,
520                const char *data,
521                uint64_t off,
522                size_t size)
523 {
524   struct Request *request = cls;
525
526   (void) kind;
527   (void) filename;
528   (void) content_type;
529   (void) transfer_encoding;
530   if (0 == strcmp ("domain", key))
531   {
532     if (size + off >= sizeof(request->domain_name))
533       size = sizeof(request->domain_name) - off - 1;
534     GNUNET_memcpy (&request->domain_name[off],
535                    data,
536                    size);
537     request->domain_name[size + off] = '\0';
538     return MHD_YES;
539   }
540   if (0 == strcmp ("pkey", key))
541   {
542     if (size + off >= sizeof(request->public_key))
543       size = sizeof(request->public_key) - off - 1;
544     GNUNET_memcpy (&request->public_key[off],
545                    data,
546                    size);
547     request->public_key[size + off] = '\0';
548     return MHD_YES;
549   }
550   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
551               _ ("Unsupported form value `%s'\n"),
552               key);
553   return MHD_YES;
554 }
555
556
557 /**
558  * Continuation called to notify client about result of the
559  * operation.
560  *
561  * @param cls closure
562  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
563  *                #GNUNET_NO if content was already there
564  *                #GNUNET_YES (or other positive value) on success
565  * @param emsg NULL on success, otherwise an error message
566  */
567 static void
568 put_continuation (void *cls,
569                   int32_t success,
570                   const char *emsg)
571 {
572   struct Request *request = cls;
573
574   request->qe = NULL;
575   if (0 >= success)
576   {
577     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
578                 _ ("Failed to create record for domain `%s': %s\n"),
579                 request->domain_name,
580                 emsg);
581     request->phase = RP_FAIL;
582   }
583   else
584     request->phase = RP_SUCCESS;
585   MHD_resume_connection (request->con);
586   run_httpd_now ();
587 }
588
589
590 /**
591  * Function called if we had an error in zone-to-name mapping.
592  */
593 static void
594 zone_to_name_error (void *cls)
595 {
596   struct Request *request = cls;
597
598   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
599               _ ("Error when mapping zone to name\n"));
600   request->phase = RP_FAIL;
601   MHD_resume_connection (request->con);
602   run_httpd_now ();
603 }
604
605
606 /**
607  * Test if a name mapping was found, if so, refuse.  If not, initiate storing of the record.
608  *
609  * @param cls closure
610  * @param zone_key public key of the zone
611  * @param name name that is being mapped (at most 255 characters long)
612  * @param rd_count number of entries in @a rd array
613  * @param rd array of records with data to store
614  */
615 static void
616 zone_to_name_cb (void *cls,
617                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
618                  const char *name,
619                  unsigned int rd_count,
620                  const struct GNUNET_GNSRECORD_Data *rd)
621 {
622   struct Request *request = cls;
623   struct GNUNET_GNSRECORD_Data r;
624
625   (void) rd;
626   (void) zone_key;
627   request->qe = NULL;
628   if (0 != rd_count)
629   {
630     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
631                 _ ("Found existing name `%s' for the given key\n"),
632                 name);
633     request->phase = RP_FAIL;
634     MHD_resume_connection (request->con);
635     run_httpd_now ();
636     return;
637   }
638   r.data = &request->pub;
639   r.data_size = sizeof(request->pub);
640   r.expiration_time = UINT64_MAX;
641   r.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
642   r.flags = GNUNET_GNSRECORD_RF_NONE;
643   request->qe = GNUNET_NAMESTORE_records_store (ns,
644                                                 &fcfs_zone_pkey,
645                                                 request->domain_name,
646                                                 1, &r,
647                                                 &put_continuation,
648                                                 request);
649 }
650
651
652 /**
653  * We encountered an error in the name lookup.
654  */
655 static void
656 lookup_it_error (void *cls)
657 {
658   struct Request *request = cls;
659
660   MHD_resume_connection (request->con);
661   request->qe = NULL;
662   request->phase = RP_FAIL;
663   run_httpd_now ();
664 }
665
666
667 /**
668  * We got a block back from the namestore.  Decrypt it
669  * and continue to process the result.
670  *
671  * @param cls the 'struct Request' we are processing
672  * @param zonekey private key of the zone; NULL on disconnect
673  * @param label label of the records; NULL on disconnect
674  * @param rd_count number of entries in @a rd array, 0 if label was deleted
675  * @param rd array of records with data to store
676  */
677 static void
678 lookup_it_processor (void *cls,
679                      const struct GNUNET_CRYPTO_EcdsaPrivateKey *zonekey,
680                      const char *label,
681                      unsigned int rd_count,
682                      const struct GNUNET_GNSRECORD_Data *rd)
683 {
684   struct Request *request = cls;
685
686   (void) label;
687   (void) rd;
688   (void) zonekey;
689   if (0 == strcmp (label, request->domain_name))
690   {
691     GNUNET_break (0 != rd_count);
692     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
693                 _ ("Found %u existing records for domain `%s'\n"),
694                 rd_count,
695                 request->domain_name);
696     request->phase = RP_FAIL;
697   }
698   GNUNET_NAMESTORE_zone_iterator_next (request->lookup_it, 1);
699 }
700
701
702 static void
703 lookup_it_finished (void *cls)
704 {
705   struct Request *request = cls;
706
707   if (RP_FAIL == request->phase)
708   {
709     MHD_resume_connection (request->con);
710     run_httpd_now ();
711     return;
712   }
713   if (GNUNET_OK !=
714       GNUNET_CRYPTO_ecdsa_public_key_from_string (request->public_key,
715                                                   strlen (request->public_key),
716                                                   &request->pub))
717   {
718     GNUNET_break (0);
719     request->phase = RP_FAIL;
720     MHD_resume_connection (request->con);
721     run_httpd_now ();
722     return;
723   }
724   request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
725                                                &fcfs_zone_pkey,
726                                                &request->pub,
727                                                &zone_to_name_error,
728                                                request,
729                                                &zone_to_name_cb,
730                                                request);
731 }
732
733
734 /**
735  * Main MHD callback for handling requests.
736  *
737  * @param cls unused
738  * @param connection MHD connection handle
739  * @param url the requested url
740  * @param method the HTTP method used ("GET", "PUT", etc.)
741  * @param version the HTTP version string (i.e. "HTTP/1.1")
742  * @param upload_data the data being uploaded (excluding HEADERS,
743  *        for a POST that fits into memory and that is encoded
744  *        with a supported encoding, the POST data will NOT be
745  *        given in upload_data and is instead available as
746  *        part of MHD_get_connection_values; very large POST
747  *        data *will* be made available incrementally in
748  *        upload_data)
749  * @param upload_data_size set initially to the size of the
750  *        @a upload_data provided; the method must update this
751  *        value to the number of bytes NOT processed;
752  * @param ptr pointer to location where we store the 'struct Request'
753  * @return #MHD_YES if the connection was handled successfully,
754  *         #MHD_NO if the socket must be closed due to a serious
755  *         error while handling the request
756  */
757 static int
758 create_response (void *cls,
759                  struct MHD_Connection *connection,
760                  const char *url,
761                  const char *method,
762                  const char *version,
763                  const char *upload_data,
764                  size_t *upload_data_size,
765                  void **ptr)
766 {
767   struct MHD_Response *response;
768   struct Request *request;
769   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
770   int ret;
771
772   (void) cls;
773   (void) version;
774   if ((0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
775       (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)))
776   {
777     if (0 == strcmp (url, FCFS_ZONEINFO_URL))
778       ret = serve_zoneinfo_page (connection);
779     else
780       ret = serve_main_page (connection);
781     if (ret != MHD_YES)
782       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
783                   _ ("Failed to create page for `%s'\n"),
784                   url);
785     return ret;
786   }
787   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
788   {
789     request = *ptr;
790     if (NULL == request)
791     {
792       request = GNUNET_new (struct Request);
793       request->con = connection;
794       *ptr = request;
795       request->pp = MHD_create_post_processor (connection,
796                                                1024,
797                                                &post_iterator,
798                                                request);
799       if (NULL == request->pp)
800       {
801         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
802                     _ ("Failed to setup post processor for `%s'\n"),
803                     url);
804         return MHD_NO;       /* internal error */
805       }
806       return MHD_YES;
807     }
808     if (NULL != request->pp)
809     {
810       /* evaluate POST data */
811       MHD_post_process (request->pp,
812                         upload_data,
813                         *upload_data_size);
814       if (0 != *upload_data_size)
815       {
816         *upload_data_size = 0;
817         return MHD_YES;
818       }
819       /* done with POST data, serve response */
820       MHD_destroy_post_processor (request->pp);
821       request->pp = NULL;
822     }
823     if (GNUNET_OK !=
824         GNUNET_CRYPTO_ecdsa_public_key_from_string (request->public_key,
825                                                     strlen (
826                                                       request->public_key),
827                                                     &pub))
828     {
829       /* parse error */
830       return fill_s_reply ("Failed to parse given public key",
831                            request, connection);
832     }
833     switch (request->phase)
834     {
835     case RP_START:
836       if (NULL != strchr (request->domain_name, (int) '.'))
837       {
838         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
839                     _ ("Domain name must not contain `.'\n"));
840         request->phase = RP_FAIL;
841         return fill_s_reply ("Domain name must not contain `.', sorry.",
842                              request,
843                              connection);
844       }
845       if (NULL != strchr (request->domain_name, (int) '+'))
846       {
847         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
848                     _ ("Domain name must not contain `+'\n"));
849         request->phase = RP_FAIL;
850         return fill_s_reply ("Domain name must not contain `+', sorry.",
851                              request, connection);
852       }
853       request->phase = RP_LOOKUP;
854       MHD_suspend_connection (request->con);
855       request->lookup_it
856         = GNUNET_NAMESTORE_zone_iteration_start (ns,
857                                                  &fcfs_zone_pkey,
858                                                  &lookup_it_error,
859                                                  request,
860                                                  &lookup_it_processor,
861                                                  request,
862                                                  &lookup_it_finished,
863                                                  request);
864       break;
865
866     case RP_LOOKUP:
867       break;
868
869     case RP_PUT:
870       break;
871
872     case RP_FAIL:
873       return fill_s_reply ("Request failed, sorry.",
874                            request, connection);
875
876     case RP_SUCCESS:
877       return fill_s_reply ("Success.",
878                            request, connection);
879
880     default:
881       GNUNET_break (0);
882       return MHD_NO;
883     }
884     return MHD_YES;   /* will have a reply later... */
885   }
886   /* unsupported HTTP method */
887   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
888                                               (void *) METHOD_ERROR,
889                                               MHD_RESPMEM_PERSISTENT);
890   ret = MHD_queue_response (connection,
891                             MHD_HTTP_NOT_ACCEPTABLE,
892                             response);
893   MHD_destroy_response (response);
894   return ret;
895 }
896
897
898 /**
899  * Callback called upon completion of a request.
900  * Decrements session reference counter.
901  *
902  * @param cls not used
903  * @param connection connection that completed
904  * @param con_cls session handle
905  * @param toe status code
906  */
907 static void
908 request_completed_callback (void *cls,
909                             struct MHD_Connection *connection,
910                             void **con_cls,
911                             enum MHD_RequestTerminationCode toe)
912 {
913   struct Request *request = *con_cls;
914
915   (void) cls;
916   (void) connection;
917   (void) toe;
918   if (NULL == request)
919     return;
920   if (NULL != request->pp)
921     MHD_destroy_post_processor (request->pp);
922   if (NULL != request->qe)
923     GNUNET_NAMESTORE_cancel (request->qe);
924   GNUNET_free (request);
925 }
926
927
928 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
929
930
931 /**
932  * Schedule tasks to run MHD server.
933  */
934 static void
935 run_httpd ()
936 {
937   fd_set rs;
938   fd_set ws;
939   fd_set es;
940   struct GNUNET_NETWORK_FDSet *wrs;
941   struct GNUNET_NETWORK_FDSet *wws;
942   struct GNUNET_NETWORK_FDSet *wes;
943   int max;
944   int haveto;
945   UNSIGNED_MHD_LONG_LONG timeout;
946   struct GNUNET_TIME_Relative tv;
947
948   FD_ZERO (&rs);
949   FD_ZERO (&ws);
950   FD_ZERO (&es);
951   wrs = GNUNET_NETWORK_fdset_create ();
952   wes = GNUNET_NETWORK_fdset_create ();
953   wws = GNUNET_NETWORK_fdset_create ();
954   max = -1;
955   GNUNET_assert (MHD_YES ==
956                  MHD_get_fdset (httpd,
957                                 &rs,
958                                 &ws,
959                                 &es,
960                                 &max));
961   haveto = MHD_get_timeout (httpd,
962                             &timeout);
963   if (haveto == MHD_YES)
964     tv.rel_value_us = (uint64_t) timeout * 1000LL;
965   else
966     tv = GNUNET_TIME_UNIT_FOREVER_REL;
967   GNUNET_NETWORK_fdset_copy_native (wrs,
968                                     &rs,
969                                     max + 1);
970   GNUNET_NETWORK_fdset_copy_native (wws,
971                                     &ws,
972                                     max + 1);
973   GNUNET_NETWORK_fdset_copy_native (wes,
974                                     &es,
975                                     max + 1);
976   httpd_task =
977     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
978                                  tv,
979                                  wrs,
980                                  wws,
981                                  &do_httpd,
982                                  NULL);
983   GNUNET_NETWORK_fdset_destroy (wrs);
984   GNUNET_NETWORK_fdset_destroy (wws);
985   GNUNET_NETWORK_fdset_destroy (wes);
986 }
987
988
989 /**
990  * Task run whenever HTTP server operations are pending.
991  *
992  * @param cls unused
993  */
994 static void
995 do_httpd (void *cls)
996 {
997   (void) cls;
998   httpd_task = NULL;
999   MHD_run (httpd);
1000   run_httpd ();
1001 }
1002
1003
1004 /**
1005  * Task run on shutdown.  Cleans up everything.
1006  *
1007  * @param cls unused
1008  */
1009 static void
1010 do_shutdown (void *cls)
1011 {
1012   (void) cls;
1013   if (NULL != httpd_task)
1014   {
1015     GNUNET_SCHEDULER_cancel (httpd_task);
1016     httpd_task = NULL;
1017   }
1018   if (NULL != uzp_task)
1019   {
1020     GNUNET_SCHEDULER_cancel (uzp_task);
1021     uzp_task = NULL;
1022   }
1023   if (NULL != ns)
1024   {
1025     GNUNET_NAMESTORE_disconnect (ns);
1026     ns = NULL;
1027   }
1028   if (NULL != httpd)
1029   {
1030     MHD_stop_daemon (httpd);
1031     httpd = NULL;
1032   }
1033   if (NULL != id_op)
1034   {
1035     GNUNET_IDENTITY_cancel (id_op);
1036     id_op = NULL;
1037   }
1038   if (NULL != identity)
1039   {
1040     GNUNET_IDENTITY_disconnect (identity);
1041     identity = NULL;
1042   }
1043 }
1044
1045
1046 /**
1047  * Method called to inform about the egos of this peer.
1048  *
1049  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
1050  * this function is only called ONCE, and 'NULL' being passed in
1051  * @a ego does indicate an error (i.e. name is taken or no default
1052  * value is known).  If @a ego is non-NULL and if '*ctx'
1053  * is set in those callbacks, the value WILL be passed to a subsequent
1054  * call to the identity callback of #GNUNET_IDENTITY_connect (if
1055  * that one was not NULL).
1056  *
1057  * @param cls closure, NULL
1058  * @param ego ego handle
1059  * @param ctx context for application to store data for this ego
1060  *                 (during the lifetime of this process, initially NULL)
1061  * @param name name assigned by the user for this ego,
1062  *                   NULL if the user just deleted the ego and it
1063  *                   must thus no longer be used
1064  */
1065 static void
1066 identity_cb (void *cls,
1067              struct GNUNET_IDENTITY_Ego *ego,
1068              void **ctx,
1069              const char *name)
1070 {
1071   int options;
1072
1073   (void) cls;
1074   (void) ctx;
1075   if (NULL == name)
1076     return;
1077   if (0 != strcmp (name,
1078                    zone))
1079     return;
1080   if (NULL == ego)
1081   {
1082     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1083                 _ ("No ego configured for `fcfsd` subsystem\n"));
1084     GNUNET_SCHEDULER_shutdown ();
1085     return;
1086   }
1087   fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
1088
1089   options = MHD_USE_DUAL_STACK | MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME;
1090   do
1091   {
1092     httpd = MHD_start_daemon (options,
1093                               (uint16_t) port,
1094                               NULL, NULL,
1095                               &create_response, NULL,
1096                               MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
1097                               MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned
1098                                                                    int) 1,
1099                               MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
1100                               MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4
1101                                                                             *
1102                                                                             1024),
1103                               MHD_OPTION_NOTIFY_COMPLETED,
1104                               &request_completed_callback, NULL,
1105                               MHD_OPTION_END);
1106     if (MHD_USE_DEBUG == options)
1107       break;
1108     options = MHD_USE_DEBUG;
1109   }
1110   while (NULL == httpd);
1111   if (NULL == httpd)
1112   {
1113     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1114                 _ ("Failed to start HTTP server\n"));
1115     GNUNET_SCHEDULER_shutdown ();
1116     return;
1117   }
1118   run_httpd ();
1119 }
1120
1121
1122 /**
1123  * Main function that will be run.
1124  *
1125  * @param cls closure
1126  * @param args remaining command-line arguments
1127  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1128  * @param cfg configuration
1129  */
1130 static void
1131 run (void *cls,
1132      char *const *args,
1133      const char *cfgfile,
1134      const struct GNUNET_CONFIGURATION_Handle *cfg)
1135 {
1136   (void) cls;
1137   (void) args;
1138   (void) cfgfile;
1139   if (GNUNET_OK !=
1140       GNUNET_CONFIGURATION_get_value_number (cfg,
1141                                              "fcfsd",
1142                                              "HTTPPORT",
1143                                              &port))
1144   {
1145     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1146                                "fcfsd", "HTTPPORT");
1147     return;
1148   }
1149   ns = GNUNET_NAMESTORE_connect (cfg);
1150   if (NULL == ns)
1151   {
1152     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1153                 _ ("Failed to connect to namestore\n"));
1154     return;
1155   }
1156   identity = GNUNET_IDENTITY_connect (cfg,
1157                                       &identity_cb,
1158                                       NULL);
1159   if (NULL == identity)
1160   {
1161     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1162                 _ ("Failed to connect to identity\n"));
1163     return;
1164   }
1165   uzp_task = GNUNET_SCHEDULER_add_now (&update_zoneinfo_page,
1166                                        NULL);
1167   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1168                                  NULL);
1169 }
1170
1171
1172 /**
1173  * The main function for the fcfs daemon.
1174  *
1175  * @param argc number of arguments from the command line
1176  * @param argv command line arguments
1177  * @return 0 ok, 1 on error
1178  */
1179 int
1180 main (int argc,
1181       char *const *argv)
1182 {
1183   struct GNUNET_GETOPT_CommandLineOption options[] = {
1184     GNUNET_GETOPT_option_mandatory
1185       (GNUNET_GETOPT_option_string ('z',
1186                                     "zone",
1187                                     "EGO",
1188                                     gettext_noop (
1189                                       "name of the zone that is to be managed by FCFSD"),
1190                                     &zone)),
1191     GNUNET_GETOPT_OPTION_END
1192   };
1193   int ret;
1194
1195   if (GNUNET_OK !=
1196       GNUNET_STRINGS_get_utf8_args (argc, argv,
1197                                     &argc, &argv))
1198     return 2;
1199
1200   GNUNET_log_setup ("fcfsd",
1201                     "WARNING",
1202                     NULL);
1203   ret =
1204     (GNUNET_OK ==
1205      GNUNET_PROGRAM_run (argc,
1206                          argv,
1207                          "gnunet-namestore-fcfsd",
1208                          _ (
1209                            "GNU Name System First Come First Serve name registration service"),
1210                          options,
1211                          &run, NULL)) ? 0 : 1;
1212   GNUNET_free ((void *) argv);
1213   GNUNET_CRYPTO_ecdsa_key_clear (&fcfs_zone_pkey);
1214   return ret;
1215 }
1216
1217
1218 /* end of gnunet-namestore-fcfsd.c */