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