- make executable, buildbots fail otherwise
[oweals/gnunet.git] / src / gns / gnunet-gns-fcfsd.c
1 /*
2      This file is part of GNUnet.
3      (C) 2012 Christian Grothoff (and other contributing authors)
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., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20 /**
21  * @file gnunet-gns-fcfsd.c
22  * @brief HTTP daemon that offers first-come-first-serve GNS domain registration
23  * @author Christian Grothoff
24  *
25  * TODO:
26  * - the code currently contains a 'race' between checking that the
27  *   domain name is available and allocating it to the new public key
28  *   (should this race be solved by namestore or by fcfsd?)
29  * - nicer error reporting to browser
30  * - figure out where this binary should go (is gns the right directory!?)
31  */
32 #include "platform.h"
33 #include <gnunet_util_lib.h>
34 #include <microhttpd.h>
35 #include <gnunet_namestore_service.h>
36
37 /**
38  * Invalid method page.
39  */
40 #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>"
41
42 /**
43  * Front page. (/)
44  */
45 #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? (63 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\" /></body></html>"
46
47 /**
48  * Second page (/S)
49  */
50 #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>"
51
52 /**
53  * Mime type for HTML pages.
54  */
55 #define MIME_HTML "text/html"
56
57 /**
58  * Name of our cookie.
59  */
60 #define COOKIE_NAME "gns-fcfs"
61
62
63 /**
64  * Phases a request goes through.
65  */
66 enum Phase
67   {
68     /**
69      * Start phase (parsing POST, checking).
70      */
71     RP_START = 0,
72
73     /**
74      * Lookup to see if the domain name is taken.
75      */
76     RP_LOOKUP,
77
78     /**
79      * Storing of the record.
80      */
81     RP_PUT,
82
83     /**
84      * We're done with success.
85      */
86     RP_SUCCESS,
87
88     /**
89      * Send failure message.
90      */
91     RP_FAIL
92   };
93
94
95 /**
96  * Data kept per request.
97  */
98 struct Request
99 {
100
101   /**
102    * Associated session.
103    */
104   struct Session *session;
105
106   /**
107    * Post processor handling form data (IF this is
108    * a POST request).
109    */
110   struct MHD_PostProcessor *pp;
111
112   /**
113    * URL to serve in response to this POST (if this request 
114    * was a 'POST')
115    */
116   const char *post_url;
117
118   /**
119    * Active request with the namestore.
120    */
121   struct GNUNET_NAMESTORE_QueueEntry *qe;
122   
123   /**
124    * Current processing phase.
125    */
126   enum Phase phase;
127
128   /**
129    * Domain name submitted via form.
130    */
131   char domain_name[64];
132
133   /**
134    * Public key submitted via form.
135    */
136   char public_key[64];
137
138 };
139
140
141 /**
142  * MHD deamon reference.
143  */
144 static struct MHD_Daemon *httpd;
145
146 /**
147  * Main HTTP task.
148  */
149 static GNUNET_SCHEDULER_TaskIdentifier httpd_task;
150
151 /**
152  * Handle to the namestore.
153  */
154 static struct GNUNET_NAMESTORE_Handle *ns;
155
156 /**
157  * Hash of the public key of the fcfsd zone.
158  */
159 static struct GNUNET_CRYPTO_ShortHashCode fcfsd_zone;
160
161 /**
162  * Private key for the fcfsd zone.
163  */
164 static struct GNUNET_CRYPTO_RsaPrivateKey *fcfs_zone_pkey;
165                         
166
167 /**
168  * Handler that returns a simple static HTTP page.
169  *
170  * @param connection connection to use
171  * @return MHD_YES on success
172  */
173 static int
174 serve_main_page (struct MHD_Connection *connection)
175 {
176   int ret;
177   struct MHD_Response *response;
178
179   /* return static form */
180   response = MHD_create_response_from_buffer (strlen (MAIN_PAGE),
181                                               (void *) MAIN_PAGE,
182                                               MHD_RESPMEM_PERSISTENT);
183   MHD_add_response_header (response,
184                            MHD_HTTP_HEADER_CONTENT_TYPE,
185                            MIME_HTML);
186   ret = MHD_queue_response (connection, 
187                             MHD_HTTP_OK, 
188                             response);
189   MHD_destroy_response (response);
190   return ret;
191 }
192
193
194 /**
195  * Send the 'SUBMIT_PAGE'.
196  *
197  * @param info information string to send to the user
198  * @param request request information
199  * @param connection connection to use
200  */
201 static int
202 fill_s_reply (const char *info,
203               struct Request *request,
204               struct MHD_Connection *connection)
205 {
206   int ret;
207   char *reply;
208   struct MHD_Response *response;
209
210   GNUNET_asprintf (&reply,
211                    SUBMIT_PAGE,
212                    info,
213                    info);
214   /* return static form */
215   response = MHD_create_response_from_buffer (strlen (reply),
216                                               (void *) reply,
217                                               MHD_RESPMEM_MUST_FREE);
218   MHD_add_response_header (response,
219                            MHD_HTTP_HEADER_CONTENT_TYPE,
220                            MIME_HTML);
221   ret = MHD_queue_response (connection, 
222                             MHD_HTTP_OK, 
223                             response);
224   MHD_destroy_response (response);
225   return ret;
226 }
227
228
229 /**
230  * Iterator over key-value pairs where the value
231  * maybe made available in increments and/or may
232  * not be zero-terminated.  Used for processing
233  * POST data.
234  *
235  * @param cls user-specified closure
236  * @param kind type of the value
237  * @param key 0-terminated key for the value
238  * @param filename name of the uploaded file, NULL if not known
239  * @param content_type mime-type of the data, NULL if not known
240  * @param transfer_encoding encoding of the data, NULL if not known
241  * @param data pointer to size bytes of data at the
242  *              specified offset
243  * @param off offset of data in the overall value
244  * @param size number of bytes in data available
245  * @return MHD_YES to continue iterating,
246  *         MHD_NO to abort the iteration
247  */
248 static int
249 post_iterator (void *cls,
250                enum MHD_ValueKind kind,
251                const char *key,
252                const char *filename,
253                const char *content_type,
254                const char *transfer_encoding,
255                const char *data, uint64_t off, size_t size)
256 {
257   struct Request *request = cls;
258
259   if (0 == strcmp ("domain", key))
260     {
261       if (size + off >= sizeof(request->domain_name))
262         size = sizeof (request->domain_name) - off - 1;
263       memcpy (&request->domain_name[off],
264               data,
265               size);
266       request->domain_name[size+off] = '\0';
267       return MHD_YES;
268     }
269   if (0 == strcmp ("pkey", key))
270     {
271       if (size + off >= sizeof(request->public_key))
272         size = sizeof (request->public_key) - off - 1;
273       memcpy (&request->public_key[off],
274               data,
275               size);
276       request->public_key[size+off] = '\0';
277       return MHD_YES;
278     }
279   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
280               _("Unsupported form value `%s'\n"),
281               key);
282   return MHD_YES;
283 }
284
285
286 /**
287  * Task run whenever HTTP server operations are pending.
288  *
289  * @param cls unused
290  * @param tc scheduler context
291  */
292 static void
293 do_httpd (void *cls,
294           const struct GNUNET_SCHEDULER_TaskContext *tc);
295
296
297 /**
298  * Schedule task to run MHD server now.
299  */
300 static void
301 run_httpd_now ()
302 {
303   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
304   {
305     GNUNET_SCHEDULER_cancel (httpd_task);
306     httpd_task = GNUNET_SCHEDULER_NO_TASK;
307   }
308   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd, NULL);
309 }
310
311
312 /**
313  * Continuation called to notify client about result of the
314  * operation.
315  *
316  * @param cls closure
317  * @param success GNUNET_SYSERR on failure (including timeout/queue drop/failure to validate)
318  *                GNUNET_NO if content was already there
319  *                GNUNET_YES (or other positive value) on success
320  * @param emsg NULL on success, otherwise an error message
321  */
322 static void 
323 put_continuation (void *cls,
324                   int32_t success,
325                   const char *emsg)
326 {
327   struct Request *request = cls;
328
329   request->qe = NULL;
330   if (0 >= success)
331   {
332     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
333                 _("Failed to create record for domain `%s': %s\n"),
334                 request->domain_name,
335                 emsg);
336     request->phase = RP_FAIL;
337   }
338   else
339     request->phase = RP_SUCCESS;
340   run_httpd_now ();
341 }
342
343
344 /**
345  * Test if a name mapping was found, if so, refuse.  If not, initiate storing of the record.
346  *
347  * @param cls closure
348  * @param zone_key public key of the zone
349  * @param expire when does the corresponding block in the DHT expire (until
350  *               when should we never do a DHT lookup for the same name again)?; 
351  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
352  *               or the expiration time of the block in the namestore (even if there are zero
353  *               records matching the desired record type)
354  * @param name name that is being mapped (at most 255 characters long)
355  * @param rd_count number of entries in 'rd' array
356  * @param rd array of records with data to store
357  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
358  *        because the user queried for a particular record type only)
359  */
360 static void 
361 zone_to_name_cb (void *cls,
362                  const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
363                  struct GNUNET_TIME_Absolute expire,                        
364                  const char *name,
365                  unsigned int rd_count,
366                  const struct GNUNET_NAMESTORE_RecordData *rd,
367                  const struct GNUNET_CRYPTO_RsaSignature *signature)
368 {
369   struct Request *request = cls;
370   struct GNUNET_NAMESTORE_RecordData r;
371   struct GNUNET_CRYPTO_ShortHashCode pub;
372   
373   request->qe = NULL;
374   if (NULL != name)
375   {
376     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
377                 _("Found existing name `%s' for the given key\n"),
378                 name);
379     request->phase = RP_FAIL;
380     run_httpd_now ();
381     return;
382   }
383   GNUNET_assert (GNUNET_OK ==
384                  GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
385                                                         strlen (request->public_key),
386                                                         &pub));
387   r.data = &pub;
388   r.data_size = sizeof (pub);
389   r.expiration_time = UINT64_MAX;
390   r.record_type = GNUNET_NAMESTORE_TYPE_PKEY;
391   r.flags = GNUNET_NAMESTORE_RF_AUTHORITY;
392   request->qe = GNUNET_NAMESTORE_record_create (ns,
393                                                 fcfs_zone_pkey,
394                                                 request->domain_name,
395                                                 &r,
396                                                 &put_continuation,
397                                                 request);
398 }
399
400
401 /**
402  * Process a record that was stored in the namestore.  Used to check if
403  * the requested name already exists in the namestore.  If not,
404  * proceed to check if the requested key already exists.
405  *
406  * @param cls closure
407  * @param zone_key public key of the zone
408  * @param expire when does the corresponding block in the DHT expire (until
409  *               when should we never do a DHT lookup for the same name again)?; 
410  *               GNUNET_TIME_UNIT_ZERO_ABS if there are no records of any type in the namestore,
411  *               or the expiration time of the block in the namestore (even if there are zero
412  *               records matching the desired record type)
413  * @param name name that is being mapped (at most 255 characters long)
414  * @param rd_count number of entries in 'rd' array
415  * @param rd array of records with data to store
416  * @param signature signature of the record block, NULL if signature is unavailable (i.e. 
417  *        because the user queried for a particular record type only)
418  */
419 static void 
420 lookup_result_processor (void *cls,
421                          const struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded *zone_key,
422                          struct GNUNET_TIME_Absolute expire,                        
423                          const char *name,
424                          unsigned int rd_count,
425                          const struct GNUNET_NAMESTORE_RecordData *rd,
426                          const struct GNUNET_CRYPTO_RsaSignature *signature)
427 {
428   struct Request *request = cls;
429   struct GNUNET_CRYPTO_ShortHashCode pub;
430   
431   request->qe = NULL;
432   GNUNET_assert (GNUNET_OK ==
433                  GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
434                                                         strlen (request->public_key),
435                                                         &pub));
436   if (0 != rd_count)
437   {
438     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
439                 _("Found %u existing records for domain `%s'\n"),
440                 rd_count,
441                 request->domain_name);
442     request->phase = RP_FAIL;
443     run_httpd_now ();
444     return;
445   }
446   request->qe = GNUNET_NAMESTORE_zone_to_name (ns,
447                                                &fcfsd_zone,
448                                                &pub,
449                                                &zone_to_name_cb,
450                                                request);
451 }
452
453
454 /**
455  * Main MHD callback for handling requests.
456  *
457  * @param cls unused
458  * @param connection MHD connection handle
459  * @param url the requested url
460  * @param method the HTTP method used ("GET", "PUT", etc.)
461  * @param version the HTTP version string (i.e. "HTTP/1.1")
462  * @param upload_data the data being uploaded (excluding HEADERS,
463  *        for a POST that fits into memory and that is encoded
464  *        with a supported encoding, the POST data will NOT be
465  *        given in upload_data and is instead available as
466  *        part of MHD_get_connection_values; very large POST
467  *        data *will* be made available incrementally in
468  *        upload_data)
469  * @param upload_data_size set initially to the size of the
470  *        upload_data provided; the method must update this
471  *        value to the number of bytes NOT processed;
472  * @param ptr pointer to location where we store the 'struct Request'
473  * @return MHD_YES if the connection was handled successfully,
474  *         MHD_NO if the socket must be closed due to a serious
475  *         error while handling the request
476  */
477 static int
478 create_response (void *cls,
479                  struct MHD_Connection *connection,
480                  const char *url,
481                  const char *method,
482                  const char *version,
483                  const char *upload_data, 
484                  size_t *upload_data_size,
485                  void **ptr)
486 {
487   struct MHD_Response *response;
488   struct Request *request;
489   int ret;
490   struct GNUNET_CRYPTO_ShortHashCode pub;
491
492   if ( (0 == strcmp (method, MHD_HTTP_METHOD_GET)) ||
493        (0 == strcmp (method, MHD_HTTP_METHOD_HEAD)) )
494     {
495       ret = serve_main_page (connection);
496       if (ret != MHD_YES)
497         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
498                     _("Failed to create page for `%s'\n"),
499                     url);
500       return ret;
501     }
502   if (0 == strcmp (method, MHD_HTTP_METHOD_POST))
503     {   
504       request = *ptr;
505       if (NULL == request)
506       {
507         request = GNUNET_malloc (sizeof (struct Request));
508         *ptr = request;
509         request->pp = MHD_create_post_processor (connection, 1024,
510                                                  &post_iterator, request);
511         if (NULL == request->pp)
512           {
513             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
514                         _("Failed to setup post processor for `%s'\n"),
515                         url);
516             return MHD_NO; /* internal error */
517           }    
518         return MHD_YES;
519       }
520       if (NULL != request->pp)
521       {
522         /* evaluate POST data */
523         MHD_post_process (request->pp,
524                           upload_data,
525                           *upload_data_size);
526         if (0 != *upload_data_size)
527           {
528             *upload_data_size = 0;
529             return MHD_YES;
530           }
531         /* done with POST data, serve response */
532         MHD_destroy_post_processor (request->pp);
533         request->pp = NULL;
534       }
535       if (GNUNET_OK !=
536           GNUNET_CRYPTO_short_hash_from_string2 (request->public_key,
537                                                  strlen (request->public_key),
538                                                  &pub))
539       {
540         /* parse error */
541         return fill_s_reply ("Failed to parse given public key",
542                              request, connection);
543       }
544       switch (request->phase)
545         {
546         case RP_START:
547           if (NULL != strchr (request->domain_name, (int) '.'))
548           {
549             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
550                         _("Domain name must not contain `.'\n"));
551             request->phase = RP_FAIL;
552             return fill_s_reply ("Domain name must not contain `.', sorry.",
553                                  request, connection);
554           }
555           if (NULL != strchr (request->domain_name, (int) '+'))
556           {
557             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
558                         _("Domain name must not contain `+'\n"));
559             request->phase = RP_FAIL;
560             return fill_s_reply ("Domain name must not contain `+', sorry.",
561                                  request, connection);
562           }
563           request->phase = RP_LOOKUP;
564           request->qe = GNUNET_NAMESTORE_lookup_record (ns,
565                                                         &fcfsd_zone,
566                                                         request->domain_name,
567                                                         GNUNET_NAMESTORE_TYPE_PKEY,
568                                                         &lookup_result_processor,
569                                                         request);
570           break;
571         case RP_LOOKUP:
572           break;
573         case RP_PUT:
574           break;
575         case RP_FAIL:
576           return fill_s_reply ("Request failed, sorry.",
577                                request, connection);
578         case RP_SUCCESS:
579           return fill_s_reply ("Success.",
580                                request, connection);
581         default:
582           GNUNET_break (0);
583           return MHD_NO;
584         }
585         return MHD_YES; /* will have a reply later... */    
586     }
587   /* unsupported HTTP method */
588   response = MHD_create_response_from_buffer (strlen (METHOD_ERROR),
589                                               (void *) METHOD_ERROR,
590                                               MHD_RESPMEM_PERSISTENT);
591   ret = MHD_queue_response (connection, 
592                             MHD_HTTP_METHOD_NOT_ACCEPTABLE, 
593                             response);
594   MHD_destroy_response (response);
595   return ret;
596 }
597
598
599 /**
600  * Callback called upon completion of a request.
601  * Decrements session reference counter.
602  *
603  * @param cls not used
604  * @param connection connection that completed
605  * @param con_cls session handle
606  * @param toe status code
607  */
608 static void
609 request_completed_callback (void *cls,
610                             struct MHD_Connection *connection,
611                             void **con_cls,
612                             enum MHD_RequestTerminationCode toe)
613 {
614   struct Request *request = *con_cls;
615
616   if (NULL == request)
617     return;
618   if (NULL != request->pp)
619     MHD_destroy_post_processor (request->pp);
620   if (NULL != request->qe)
621     GNUNET_NAMESTORE_cancel (request->qe);
622   GNUNET_free (request);
623 }
624
625
626 /**
627  * Schedule tasks to run MHD server.
628  */
629 static void
630 run_httpd ()
631 {
632   fd_set rs;
633   fd_set ws;
634   fd_set es;
635   struct GNUNET_NETWORK_FDSet *wrs;
636   struct GNUNET_NETWORK_FDSet *wws;
637   struct GNUNET_NETWORK_FDSet *wes;
638   int max;
639   int haveto;
640   unsigned MHD_LONG_LONG timeout;
641   struct GNUNET_TIME_Relative tv;
642
643   FD_ZERO (&rs);
644   FD_ZERO (&ws);
645   FD_ZERO (&es);
646   wrs = GNUNET_NETWORK_fdset_create ();
647   wes = GNUNET_NETWORK_fdset_create ();
648   wws = GNUNET_NETWORK_fdset_create ();
649   max = -1;
650   GNUNET_assert (MHD_YES == MHD_get_fdset (httpd, &rs, &ws, &es, &max));
651   haveto = MHD_get_timeout (httpd, &timeout);
652   if (haveto == MHD_YES)
653     tv.rel_value = (uint64_t) timeout;
654   else
655     tv = GNUNET_TIME_UNIT_FOREVER_REL;
656   GNUNET_NETWORK_fdset_copy_native (wrs, &rs, max + 1);
657   GNUNET_NETWORK_fdset_copy_native (wws, &ws, max + 1);
658   GNUNET_NETWORK_fdset_copy_native (wes, &es, max + 1);
659   httpd_task =
660       GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH,
661                                    tv, wrs, wws,
662                                    &do_httpd, NULL);
663   GNUNET_NETWORK_fdset_destroy (wrs);
664   GNUNET_NETWORK_fdset_destroy (wws);
665   GNUNET_NETWORK_fdset_destroy (wes);
666 }
667
668
669 /**
670  * Task run whenever HTTP server operations are pending.
671  *
672  * @param cls unused
673  * @param tc scheduler context
674  */
675 static void
676 do_httpd (void *cls,
677           const struct GNUNET_SCHEDULER_TaskContext *tc)
678 {
679   httpd_task = GNUNET_SCHEDULER_NO_TASK;
680   MHD_run (httpd);
681   run_httpd ();
682 }
683
684
685 /**
686  * Task run on shutdown.  Cleans up everything.
687  *
688  * @param cls unused
689  * @param tc scheduler context
690  */
691 static void
692 do_shutdown (void *cls,
693              const struct GNUNET_SCHEDULER_TaskContext *tc)
694 {
695   if (GNUNET_SCHEDULER_NO_TASK != httpd_task)
696   {
697     GNUNET_SCHEDULER_cancel (httpd_task);
698     httpd_task = GNUNET_SCHEDULER_NO_TASK;
699   }
700   if (NULL != ns)
701   {
702     GNUNET_NAMESTORE_disconnect (ns);
703     ns = NULL;
704   }
705   if (NULL != httpd)
706   {
707     MHD_stop_daemon (httpd);
708     httpd = NULL;
709   }
710   if (NULL != fcfs_zone_pkey)
711   {
712     GNUNET_CRYPTO_rsa_key_free (fcfs_zone_pkey);
713     fcfs_zone_pkey = NULL;
714   }
715 }
716
717
718 /**
719  * Main function that will be run.
720  *
721  * @param cls closure
722  * @param args remaining command-line arguments
723  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
724  * @param cfg configuration
725  */
726 static void
727 run (void *cls, char *const *args, const char *cfgfile,
728      const struct GNUNET_CONFIGURATION_Handle *cfg)
729 {
730   char *keyfile;
731   unsigned long long port;
732   struct GNUNET_CRYPTO_RsaPublicKeyBinaryEncoded pub;
733
734   if (GNUNET_OK !=
735       GNUNET_CONFIGURATION_get_value_number (cfg,
736                                              "fcfsd",
737                                              "HTTPPORT",
738                                              &port))
739   {
740       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
741                   _("Option `%s' not specified in configuration section `%s'\n"),
742                   "HTTPPORT",
743                   "fcfsd");
744       return;
745   }
746   if (GNUNET_OK !=
747       GNUNET_CONFIGURATION_get_value_filename (cfg,
748                                                "fcfsd",
749                                                "ZONEKEY",
750                                                &keyfile))
751   {
752     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
753                 _("Option `%s' not specified in configuration section `%s'\n"),
754                 "ZONEKEY",
755                 "fcfsd");
756     return;
757   }
758   fcfs_zone_pkey = GNUNET_CRYPTO_rsa_key_create_from_file (keyfile);
759   GNUNET_free (keyfile);
760   if (NULL == fcfs_zone_pkey)
761   {
762     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
763                 _("Failed to read or create private zone key\n"));
764     return;
765   }
766   GNUNET_CRYPTO_rsa_key_get_public (fcfs_zone_pkey,
767                                     &pub);
768   GNUNET_CRYPTO_short_hash (&pub, sizeof (pub), &fcfsd_zone);
769   ns = GNUNET_NAMESTORE_connect (cfg);
770   if (NULL == ns)
771     {
772       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
773                   _("Failed to connect to namestore\n"));
774       return;
775     }
776   httpd = MHD_start_daemon (MHD_USE_DEBUG,
777                             (uint16_t) port,
778                             NULL, NULL, 
779                             &create_response, NULL, 
780                             MHD_OPTION_CONNECTION_LIMIT, (unsigned int) 128,
781                             MHD_OPTION_PER_IP_CONNECTION_LIMIT, (unsigned int) 1,
782                             MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
783                             MHD_OPTION_CONNECTION_MEMORY_LIMIT, (size_t) (4 * 1024), 
784                             MHD_OPTION_NOTIFY_COMPLETED, &request_completed_callback, NULL,
785                             MHD_OPTION_END);
786   if (NULL == httpd)
787   {
788     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
789                 _("Failed to start HTTP server\n"));
790     GNUNET_NAMESTORE_disconnect (ns);
791     ns = NULL;
792     return;
793   }
794   run_httpd ();
795   GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
796                                 &do_shutdown, NULL);
797 }
798
799
800 /**
801  * The main function for the fcfs daemon.
802  *
803  * @param argc number of arguments from the command line
804  * @param argv command line arguments
805  * @return 0 ok, 1 on error
806  */
807 int
808 main (int argc, char *const *argv)
809 {
810   static const struct GNUNET_GETOPT_CommandLineOption options[] = {
811     GNUNET_GETOPT_OPTION_END
812   };
813
814   int ret;
815
816   if (GNUNET_OK != GNUNET_STRINGS_get_utf8_args (argc, argv, &argc, &argv))
817     return 2;
818
819   GNUNET_log_setup ("fcfsd", "WARNING", NULL);
820   ret =
821       (GNUNET_OK ==
822        GNUNET_PROGRAM_run (argc, argv, "fcfsd",
823                            _("GNUnet GNS first come first serve registration service"), 
824                            options,
825                            &run, NULL)) ? 0 : 1;
826
827   return ret;
828 }
829
830 /* end of gnunet-gns-fcfsd.c */