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