rename connecT -> connect now that the old API is dead
[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     return;
331   }
332
333   if (GNUNET_GNSRECORD_TYPE_PKEY != rd->record_type)
334   {
335     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
336     return;
337   }
338
339   bytes_free = zr->buf_len - zr->write_offset;
340   pkey = GNUNET_GNSRECORD_value_to_string (rd->record_type,
341                                            rd->data,
342                                            rd->data_size);
343   if (NULL == pkey)
344   {
345     GNUNET_break (0);
346     GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
347     return;
348   }
349   if (bytes_free < (strlen (name) + strlen (pkey) + 40))
350   {
351     new_buf = GNUNET_malloc (zr->buf_len * 2);
352     GNUNET_memcpy (new_buf, zr->zoneinfo, zr->write_offset);
353     GNUNET_free (zr->zoneinfo);
354     zr->zoneinfo = new_buf;
355     zr->buf_len *= 2;
356   }
357   sprintf (zr->zoneinfo + zr->write_offset,
358            "<tr><td>%s</td><td>%s</td></tr>",
359            name,
360            pkey);
361   zr->write_offset = strlen (zr->zoneinfo);
362   GNUNET_NAMESTORE_zone_iterator_next (zr->list_it);
363   GNUNET_free (pkey);
364 }
365
366
367 /**
368  * Handler that returns FCFS zoneinfo page.
369  *
370  * @param connection connection to use
371  * @return MHD_YES on success
372  */
373 static int
374 serve_zoneinfo_page (struct MHD_Connection *connection)
375 {
376   struct ZoneinfoRequest *zr;
377
378   zr = GNUNET_new (struct ZoneinfoRequest);
379   zr->zoneinfo = GNUNET_malloc (DEFAULT_ZONEINFO_BUFSIZE);
380   zr->buf_len = DEFAULT_ZONEINFO_BUFSIZE;
381   zr->connection = connection;
382   zr->write_offset = 0;
383   zr->list_it = GNUNET_NAMESTORE_zone_iteration_start (ns,
384                                                        &fcfs_zone_pkey,
385                                                        &zone_iteration_error,
386                                                        zr,
387                                                        &iterate_cb,
388                                                        zr,
389                                                        &zone_iteration_end,
390                                                        zr);
391   return MHD_YES;
392 }
393
394
395 /**
396  * Handler that returns a simple static HTTP page.
397  *
398  * @param connection connection to use
399  * @return MHD_YES on success
400  */
401 static int
402 serve_main_page (struct MHD_Connection *connection)
403 {
404   int ret;
405   struct MHD_Response *response;
406
407   /* return static form */
408   response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
409                                               (void *) MAIN_PAGE,
410                                               MHD_RESPMEM_PERSISTENT);
411   MHD_add_response_header (response,
412                            MHD_HTTP_HEADER_CONTENT_TYPE,
413                            MIME_HTML);
414   ret = MHD_queue_response (connection,
415                             MHD_HTTP_OK,
416                             response);
417   MHD_destroy_response (response);
418   return ret;
419 }
420
421
422 /**
423  * Send the 'SUBMIT_PAGE'.
424  *
425  * @param info information string to send to the user
426  * @param request request information
427  * @param connection connection to use
428  */
429 static int
430 fill_s_reply (const char *info,
431               struct Request *request,
432               struct MHD_Connection *connection)
433 {
434   int ret;
435   char *reply;
436   struct MHD_Response *response;
437
438   GNUNET_asprintf (&reply,
439                    SUBMIT_PAGE,
440                    info,
441                    info);
442   /* return static form */
443   response = MHD_create_response_from_buffer (strlen (reply),
444                                               (void *) reply,
445                                               MHD_RESPMEM_MUST_FREE);
446   MHD_add_response_header (response,
447                            MHD_HTTP_HEADER_CONTENT_TYPE,
448                            MIME_HTML);
449   ret = MHD_queue_response (connection,
450                             MHD_HTTP_OK,
451                             response);
452   MHD_destroy_response (response);
453   return ret;
454 }
455
456
457 /**
458  * Iterator over key-value pairs where the value
459  * maybe made available in increments and/or may
460  * not be zero-terminated.  Used for processing
461  * POST data.
462  *
463  * @param cls user-specified closure
464  * @param kind type of the value
465  * @param key 0-terminated key for the value
466  * @param filename name of the uploaded file, NULL if not known
467  * @param content_type mime-type of the data, NULL if not known
468  * @param transfer_encoding encoding of the data, NULL if not known
469  * @param data pointer to size bytes of data at the
470  *              specified offset
471  * @param off offset of data in the overall value
472  * @param size number of bytes in data available
473  * @return MHD_YES to continue iterating,
474  *         MHD_NO to abort the iteration
475  */
476 static int
477 post_iterator (void *cls,
478                enum MHD_ValueKind kind,
479                const char *key,
480                const char *filename,
481                const char *content_type,
482                const char *transfer_encoding,
483                const char *data, uint64_t off, size_t size)
484 {
485   struct Request *request = cls;
486
487   if (0 == strcmp ("domain", key))
488     {
489       if (size + off >= sizeof(request->domain_name))
490         size = sizeof (request->domain_name) - off - 1;
491       GNUNET_memcpy (&request->domain_name[off],
492               data,
493               size);
494       request->domain_name[size+off] = '\0';
495       return MHD_YES;
496     }
497   if (0 == strcmp ("pkey", key))
498     {
499       if (size + off >= sizeof(request->public_key))
500         size = sizeof (request->public_key) - off - 1;
501       GNUNET_memcpy (&request->public_key[off],
502               data,
503               size);
504       request->public_key[size+off] = '\0';
505       return MHD_YES;
506     }
507   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
508               _("Unsupported form value `%s'\n"),
509               key);
510   return MHD_YES;
511 }
512
513
514 /**
515  * Continuation called to notify client about result of the
516  * operation.
517  *
518  * @param cls closure
519  * @param success #GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
520  *                #GNUNET_NO if content was already there
521  *                #GNUNET_YES (or other positive value) on success
522  * @param emsg NULL on success, otherwise an error message
523  */
524 static void
525 put_continuation (void *cls,
526                   int32_t success,
527                   const char *emsg)
528 {
529   struct Request *request = cls;
530
531   request->qe = NULL;
532   if (0 >= success)
533   {
534     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
535                 _("Failed to create record for domain `%s': %s\n"),
536                 request->domain_name,
537                 emsg);
538     request->phase = RP_FAIL;
539   }
540   else
541     request->phase = RP_SUCCESS;
542   run_httpd_now ();
543 }
544
545
546 /**
547  * Function called if we had an error in zone-to-name mapping.
548  */
549 static void
550 zone_to_name_error (void *cls)
551 {
552   struct Request *request = cls;
553
554   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
555               _("Error when mapping zone to name\n"));
556   request->phase = RP_FAIL;
557   run_httpd_now ();
558 }
559
560
561 /**
562  * Test if a name mapping was found, if so, refuse.  If not, initiate storing of the record.
563  *
564  * @param cls closure
565  * @param zone_key public key of the zone
566  * @param name name that is being mapped (at most 255 characters long)
567  * @param rd_count number of entries in @a rd array
568  * @param rd array of records with data to store
569  */
570 static void
571 zone_to_name_cb (void *cls,
572                  const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone_key,
573                  const char *name,
574                  unsigned int rd_count,
575                  const struct GNUNET_GNSRECORD_Data *rd)
576 {
577   struct Request *request = cls;
578   struct GNUNET_GNSRECORD_Data r;
579
580   request->qe = NULL;
581
582   if (0 != rd_count)
583   {
584     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
585                 _("Found existing name `%s' for the given key\n"),
586                 name);
587     request->phase = RP_FAIL;
588     run_httpd_now ();
589     return;
590   }
591   r.data = &request->pub;
592   r.data_size = sizeof (request->pub);
593   r.expiration_time = UINT64_MAX;
594   r.record_type = GNUNET_GNSRECORD_TYPE_PKEY;
595   r.flags = GNUNET_GNSRECORD_RF_NONE;
596   request->qe = GNUNET_NAMESTORE_records_store (ns,
597                                                 &fcfs_zone_pkey,
598                                                 request->domain_name,
599                                                 1, &r,
600                                                 &put_continuation,
601                                                 request);
602 }
603
604
605 /**
606  * We encountered an error in the name lookup.
607  */
608 static void
609 lookup_block_error (void *cls)
610 {
611   struct Request *request = cls;
612
613   request->qe = NULL;
614   request->phase = RP_FAIL;
615   run_httpd_now ();
616 }
617
618
619 /**
620  * We got a block back from the namestore.  Decrypt it
621  * and continue to process the result.
622  *
623  * @param cls the 'struct Request' we are processing
624  * @param zone private key of the zone; NULL on disconnect
625  * @param label label of the records; NULL on disconnect
626  * @param rd_count number of entries in @a rd array, 0 if label was deleted
627  * @param rd array of records with data to store
628  */
629 static void
630 lookup_block_processor (void *cls,
631                         const struct GNUNET_CRYPTO_EcdsaPrivateKey *zone,
632                         const char *label,
633                         unsigned int rd_count,
634                         const struct GNUNET_GNSRECORD_Data *rd)
635 {
636   struct Request *request = cls;
637
638   request->qe = NULL;
639   if (0 == rd_count)
640   {
641     if (GNUNET_OK !=
642         GNUNET_CRYPTO_ecdsa_public_key_from_string (request->public_key,
643                                                     strlen (request->public_key),
644                                                     &request->pub))
645     {
646       GNUNET_break (0);
647       request->phase = RP_FAIL;
648       run_httpd_now ();
649       return;
650     }
651     request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
652                                                  &fcfs_zone_pkey,
653                                                  &request->pub,
654                                                  &zone_to_name_error,
655                                                  request,
656                                                  &zone_to_name_cb,
657                                                  request);
658     return;
659   }
660   GNUNET_break (0 != rd_count);
661   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
662               _("Found %u existing records for domain `%s'\n"),
663               rd_count,
664               request->domain_name);
665   request->phase = RP_FAIL;
666   run_httpd_now ();
667   return;
668 }
669
670
671 /**
672  * Main MHD callback for handling requests.
673  *
674  * @param cls unused
675  * @param connection MHD connection handle
676  * @param url the requested url
677  * @param method the HTTP method used ("GET", "PUT", etc.)
678  * @param version the HTTP version string (i.e. "HTTP/1.1")
679  * @param upload_data the data being uploaded (excluding HEADERS,
680  *        for a POST that fits into memory and that is encoded
681  *        with a supported encoding, the POST data will NOT be
682  *        given in upload_data and is instead available as
683  *        part of MHD_get_connection_values; very large POST
684  *        data *will* be made available incrementally in
685  *        upload_data)
686  * @param upload_data_size set initially to the size of the
687  *        @a upload_data provided; the method must update this
688  *        value to the number of bytes NOT processed;
689  * @param ptr pointer to location where we store the 'struct Request'
690  * @return MHD_YES if the connection was handled successfully,
691  *         MHD_NO if the socket must be closed due to a serious
692  *         error while handling the request
693  */
694 static int
695 create_response (void *cls,
696                  struct MHD_Connection *connection,
697                  const char *url,
698                  const char *method,
699                  const char *version,
700                  const char *upload_data,
701                  size_t *upload_data_size,
702                  void **ptr)
703 {
704   struct MHD_Response *response;
705   struct Request *request;
706   struct GNUNET_CRYPTO_EcdsaPublicKey pub;
707   int ret;
708
709   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
710        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
711     {
712       if (0 == strcmp (url, FCFS_ZONEINFO_URL))
713         ret = serve_zoneinfo_page (connection);
714       else
715         ret = serve_main_page (connection);
716       if (ret != MHD_YES)
717         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
718                     _("Failed to create page for `%s'\n"),
719                     url);
720       return ret;
721     }
722   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
723     {
724       request = *ptr;
725       if (NULL == request)
726       {
727         request = GNUNET_new (struct Request);
728         *ptr = request;
729         request->pp = MHD_create_post_processor (connection, 1024,
730                                                  &post_iterator, request);
731         if (NULL == request->pp)
732           {
733             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
734                         _("Failed to setup post processor for `%s'\n"),
735                         url);
736             return MHD_NO; /* internal error */
737           }
738         return MHD_YES;
739       }
740       if (NULL != request->pp)
741       {
742         /* evaluate POST data */
743         MHD_post_process (request->pp,
744                           upload_data,
745                           *upload_data_size);
746         if (0 != *upload_data_size)
747           {
748             *upload_data_size = 0;
749             return MHD_YES;
750           }
751         /* done with POST data, serve response */
752         MHD_destroy_post_processor (request->pp);
753         request->pp = NULL;
754       }
755       if (GNUNET_OK !=
756           GNUNET_CRYPTO_ecdsa_public_key_from_string (request->public_key,
757                                                       strlen (request->public_key),
758                                                       &pub))
759       {
760         /* parse error */
761         return fill_s_reply ("Failed to parse given public key",
762                              request, connection);
763       }
764       switch (request->phase)
765         {
766         case RP_START:
767           if (NULL != strchr (request->domain_name, (int) '.'))
768           {
769             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
770                         _("Domain name must not contain `.'\n"));
771             request->phase = RP_FAIL;
772             return fill_s_reply ("Domain name must not contain `.', sorry.",
773                                  request, connection);
774           }
775           if (NULL != strchr (request->domain_name, (int) '+'))
776           {
777             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
778                         _("Domain name must not contain `+'\n"));
779             request->phase = RP_FAIL;
780             return fill_s_reply ("Domain name must not contain `+', sorry.",
781                                  request, connection);
782           }
783           request->phase = RP_LOOKUP;
784           request->qe = GNUNET_NAMESTORE_records_lookup (ns,
785                                                          &fcfs_zone_pkey,
786                                                          request->domain_name,
787                                                          &lookup_block_error,
788                                                          request,
789                                                          &lookup_block_processor,
790                                                          request);
791           break;
792         case RP_LOOKUP:
793           break;
794         case RP_PUT:
795           break;
796         case RP_FAIL:
797           return fill_s_reply ("Request failed, sorry.",
798                                request, connection);
799         case RP_SUCCESS:
800           return fill_s_reply ("Success.",
801                                request, connection);
802         default:
803           GNUNET_break (0);
804           return MHD_NO;
805         }
806         return MHD_YES; /* will have a reply later... */
807     }
808   /* unsupported HTTP method */
809   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
810                                               (void *) METHOD_ERROR,
811                                               MHD_RESPMEM_PERSISTENT);
812   ret = MHD_queue_response (connection,
813                             MHD_HTTP_NOT_ACCEPTABLE,
814                             response);
815   MHD_destroy_response (response);
816   return ret;
817 }
818
819
820 /**
821  * Callback called upon completion of a request.
822  * Decrements session reference counter.
823  *
824  * @param cls not used
825  * @param connection connection that completed
826  * @param con_cls session handle
827  * @param toe status code
828  */
829 static void
830 request_completed_callback (void *cls,
831                             struct MHD_Connection *connection,
832                             void **con_cls,
833                             enum MHD_RequestTerminationCode toe)
834 {
835   struct Request *request = *con_cls;
836
837   if (NULL == request)
838     return;
839   if (NULL != request->pp)
840     MHD_destroy_post_processor (request->pp);
841   if (NULL != request->qe)
842     GNUNET_NAMESTORE_cancel (request->qe);
843   GNUNET_free (request);
844 }
845
846
847 #define UNSIGNED_MHD_LONG_LONG unsigned MHD_LONG_LONG
848
849
850 /**
851  * Schedule tasks to run MHD server.
852  */
853 static void
854 run_httpd ()
855 {
856   fd_set rs;
857   fd_set ws;
858   fd_set es;
859   struct GNUNET_NETWORK_FDSet *wrs;
860   struct GNUNET_NETWORK_FDSet *wws;
861   struct GNUNET_NETWORK_FDSet *wes;
862   int max;
863   int haveto;
864   UNSIGNED_MHD_LONG_LONG timeout;
865   struct GNUNET_TIME_Relative tv;
866
867   FD_ZERO (&rs);
868   FD_ZERO (&ws);
869   FD_ZERO (&es);
870   wrs = GNUNET_NETWORK_fdset_create ();
871   wes = GNUNET_NETWORK_fdset_create ();
872   wws = GNUNET_NETWORK_fdset_create ();
873   max = -1;
874   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
875   haveto = MHD_get_timeout (httpd, &timeout);
876   if (haveto == MHD_YES)
877     tv.rel_value_us = (uint64_t) timeout * 1000LL;
878   else
879     tv = GNUNET_TIME_UNIT_FOREVER_REL;
880   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
881   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
882   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
883   httpd_task =
884       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
885                                    tv, wrs, wws,
886                                    &do_httpd, NULL);
887   GNUNET_NETWORK_fdset_destroy (wrs);
888   GNUNET_NETWORK_fdset_destroy (wws);
889   GNUNET_NETWORK_fdset_destroy (wes);
890 }
891
892
893 /**
894  * Task run whenever HTTP server operations are pending.
895  *
896  * @param cls unused
897  */
898 static void
899 do_httpd (void *cls)
900 {
901   httpd_task = NULL;
902   MHD_run (httpd);
903   run_httpd ();
904 }
905
906
907 /**
908  * Task run on shutdown.  Cleans up everything.
909  *
910  * @param cls unused
911  */
912 static void
913 do_shutdown (void *cls)
914 {
915   if (NULL != httpd_task)
916   {
917     GNUNET_SCHEDULER_cancel (httpd_task);
918     httpd_task = NULL;
919   }
920   if (NULL != ns)
921   {
922     GNUNET_NAMESTORE_disconnect (ns);
923     ns = NULL;
924   }
925   if (NULL != httpd)
926   {
927     MHD_stop_daemon (httpd);
928     httpd = NULL;
929   }
930   if (NULL != id_op)
931   {
932     GNUNET_IDENTITY_cancel (id_op);
933     id_op = NULL;
934   }
935   if (NULL != identity)
936   {
937     GNUNET_IDENTITY_disconnect (identity);
938     identity = NULL;
939   }
940 }
941
942
943 /**
944  * Method called to inform about the egos of this peer.
945  *
946  * When used with #GNUNET_IDENTITY_create or #GNUNET_IDENTITY_get,
947  * this function is only called ONCE, and 'NULL' being passed in
948  * @a ego does indicate an error (i.e. name is taken or no default
949  * value is known).  If @a ego is non-NULL and if '*ctx'
950  * is set in those callbacks, the value WILL be passed to a subsequent
951  * call to the identity callback of #GNUNET_IDENTITY_connect (if
952  * that one was not NULL).
953  *
954  * @param cls closure, NULL
955  * @param ego ego handle
956  * @param ctx context for application to store data for this ego
957  *                 (during the lifetime of this process, initially NULL)
958  * @param name name assigned by the user for this ego,
959  *                   NULL if the user just deleted the ego and it
960  *                   must thus no longer be used
961  */
962 static void
963 identity_cb (void *cls,
964              struct GNUNET_IDENTITY_Ego *ego,
965              void **ctx,
966              const char *name)
967 {
968   int options;
969
970   id_op = NULL;
971   if (NULL == ego)
972   {
973     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
974                 _("No ego configured for `fcfsd` subsystem\n"));
975     return;
976   }
977   fcfs_zone_pkey = *GNUNET_IDENTITY_ego_get_private_key (ego);
978
979   options = MHD_USE_DUAL_STACK | MHD_USE_DEBUG;
980   do
981     {
982       httpd = MHD_start_daemon (options,
983                                 (uint16_t) port,
984                                 NULL, NULL,
985                                 &create_response, NULL,
986                                 MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
987                                 MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
988                                 MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
989                                 MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024),
990                                 MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
991                                 MHD_OPTION_END);
992       if (MHD_USE_DEBUG == options)
993         break;
994       options = MHD_USE_DEBUG;
995     }
996   while (NULL == httpd);
997   if (NULL == httpd)
998   {
999     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1000                 _("Failed to start HTTP server\n"));
1001     GNUNET_SCHEDULER_shutdown ();
1002     return;
1003   }
1004   run_httpd ();
1005 }
1006
1007
1008 /**
1009  * Main function that will be run.
1010  *
1011  * @param cls closure
1012  * @param args remaining command-line arguments
1013  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1014  * @param cfg configuration
1015  */
1016 static void
1017 run (void *cls, char *const *args, const char *cfgfile,
1018      const struct GNUNET_CONFIGURATION_Handle *cfg)
1019 {
1020   if (GNUNET_OK !=
1021       GNUNET_CONFIGURATION_get_value_number (cfg,
1022                                              "fcfsd",
1023                                              "HTTPPORT",
1024                                              &port))
1025   {
1026     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1027                                "fcfsd", "HTTPPORT");
1028     return;
1029   }
1030   ns = GNUNET_NAMESTORE_connect (cfg);
1031   if (NULL == ns)
1032     {
1033       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1034                   _("Failed to connect to namestore\n"));
1035       return;
1036     }
1037   identity = GNUNET_IDENTITY_connect (cfg,
1038                                       NULL, NULL);
1039   if (NULL == identity)
1040   {
1041     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1042                 _("Failed to connect to identity\n"));
1043     return;
1044   }
1045   id_op = GNUNET_IDENTITY_get (identity, "fcfsd",
1046                                &identity_cb, NULL);
1047   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, NULL);
1048 }
1049
1050
1051 /**
1052  * The main function for the fcfs daemon.
1053  *
1054  * @param argc number of arguments from the command line
1055  * @param argv command line arguments
1056  * @return 0 ok, 1 on error
1057  */
1058 int
1059 main (int argc, char *const *argv)
1060 {
1061   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
1062     GNUNET_GETOPT_OPTION_END
1063   };
1064
1065   int ret;
1066
1067   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
1068     return 2;
1069
1070   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
1071   ret =
1072       (GNUNET_OK ==
1073        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
1074                            _("GNU Name System First Come First Serve name registration service"),
1075                            options,
1076                            &run, NULL)) ? 0 : 1;
1077   GNUNET_free ((void*) argv);
1078   GNUNET_CRYPTO_ecdsa_key_clear (&fcfs_zone_pkey);
1079   return ret;
1080 }
1081
1082 /* end of gnunet-namestore-fcfsd.c */