challenger

OAuth 2.0-based authentication service that validates user can receive messages at a certain address
Log | Files | Refs | Submodules | README | LICENSE

challenger-httpd.c (25555B)


      1 /*
      2   This file is part of Challenger
      3   (C) 2023, 2024, 2025 Taler Systems SA
      4 
      5   Challenger is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   Challenger is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   Challenger; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file challenger/challenger-httpd.c
     18  * @brief OAuth 2.0 server challenging users to demonstrate ability to receive messages
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <microhttpd.h>
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_curl_lib.h>
     25 #include "challenger_util.h"
     26 #include "challenger-httpd.h"
     27 #include "challenger-httpd_agpl.h"
     28 #include "challenger-httpd_authorize.h"
     29 #include "challenger-httpd_challenge.h"
     30 #include "challenger-httpd_config.h"
     31 #include "challenger-httpd_info.h"
     32 #include "challenger-httpd_mhd.h"
     33 #include "challenger-httpd_setup.h"
     34 #include "challenger-httpd_solve.h"
     35 #include "challenger-httpd_token.h"
     36 #include "challenger-httpd_spa.h"
     37 #include "challenger_database_lib.h"
     38 #include "challenger-database/preflight.h"
     39 
     40 
     41 /**
     42  * Should a "Connection: close" header be added to each HTTP response?
     43  */
     44 static int CH_challenger_connection_close;
     45 
     46 /**
     47  * Our context for making HTTP requests.
     48  */
     49 struct GNUNET_CURL_Context *CH_ctx;
     50 
     51 /**
     52  * Reschedule context for #CH_ctx.
     53  */
     54 static struct GNUNET_CURL_RescheduleContext *rc;
     55 
     56 /**
     57  * Global return code
     58  */
     59 static int global_ret;
     60 
     61 /**
     62  * True if we started any HTTP daemon.
     63  */
     64 static bool have_daemons;
     65 
     66 struct CHALLENGERDB_PostgresContext *CH_context;
     67 
     68 char *CH_base_url;
     69 
     70 void *CH_message_template;
     71 
     72 size_t CH_message_template_len;
     73 
     74 struct GNUNET_TIME_Relative CH_validation_duration;
     75 
     76 struct GNUNET_TIME_Relative CH_validation_expiration;
     77 
     78 struct GNUNET_TIME_Relative CH_token_expiration;
     79 
     80 struct GNUNET_TIME_Relative CH_pin_retransmission_frequency;
     81 
     82 json_t *CH_restrictions;
     83 
     84 char *CH_address_type;
     85 
     86 char *CH_address_hint;
     87 
     88 char *CH_auth_command;
     89 
     90 
     91 /**
     92  * Function called first by MHD with the full URL.
     93  *
     94  * @param cls NULL
     95  * @param full_url the full URL
     96  * @param con MHD connection object
     97  * @return our handler context
     98  */
     99 static void *
    100 full_url_track_callback (void *cls,
    101                          const char *full_url,
    102                          struct MHD_Connection *con)
    103 {
    104   struct CH_HandlerContext *hc;
    105 
    106   hc = GNUNET_new (struct CH_HandlerContext);
    107   GNUNET_async_scope_fresh (&hc->async_scope_id);
    108   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    109   hc->full_url = GNUNET_strdup (full_url);
    110   return hc;
    111 }
    112 
    113 
    114 /**
    115  * A client has requested the given url using the given method
    116  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
    117  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
    118  * must call MHD callbacks to provide content to give back to the
    119  * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
    120  * #MHD_HTTP_NOT_FOUND, etc.).
    121  *
    122  * @param cls argument given together with the function
    123  *        pointer when the handler was registered with MHD
    124  * @param connection connection handle
    125  * @param url the requested url
    126  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
    127  *        #MHD_HTTP_METHOD_PUT, etc.)
    128  * @param version the HTTP version string (i.e.
    129  *        #MHD_HTTP_VERSION_1_1)
    130  * @param upload_data the data being uploaded (excluding HEADERS,
    131  *        for a POST that fits into memory and that is encoded
    132  *        with a supported encoding, the POST data will NOT be
    133  *        given in upload_data and is instead available as
    134  *        part of #MHD_get_connection_values; very large POST
    135  *        data *will* be made available incrementally in
    136  *        @a upload_data)
    137  * @param upload_data_size set initially to the size of the
    138  *        @a upload_data provided; the method must update this
    139  *        value to the number of bytes NOT processed;
    140  * @param con_cls pointer that the callback can set to some
    141  *        address and that will be preserved by MHD for future
    142  *        calls for this request; since the access handler may
    143  *        be called many times (i.e., for a PUT/POST operation
    144  *        with plenty of upload data) this allows the application
    145  *        to easily associate some request-specific state.
    146  *        If necessary, this state can be cleaned up in the
    147  *        global #MHD_RequestCompletedCallback (which
    148  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
    149  *        Initially, `*con_cls` will be NULL.
    150  * @return #MHD_YES if the connection was handled successfully,
    151  *         #MHD_NO if the socket must be closed due to a serious
    152  *         error while handling the request
    153  */
    154 static enum MHD_Result
    155 url_handler (void *cls,
    156              struct MHD_Connection *connection,
    157              const char *url,
    158              const char *method,
    159              const char *version,
    160              const char *upload_data,
    161              size_t *upload_data_size,
    162              void **con_cls)
    163 {
    164   static struct CH_RequestHandler handlers[] = {
    165     /* Landing page, tell humans to go away. */
    166     {
    167       .url = "/",
    168       .method = MHD_HTTP_METHOD_GET,
    169       .handler = &CH_spa_redirect
    170     },
    171     {
    172       .url = "/webui/",
    173       .method = MHD_HTTP_METHOD_GET,
    174       .handler = &CH_handler_spa,
    175     },
    176     {
    177       .url = "/agpl",
    178       .method = MHD_HTTP_METHOD_GET,
    179       .handler = &CH_handler_agpl
    180     },
    181     {
    182       .url = "/config",
    183       .method = MHD_HTTP_METHOD_GET,
    184       .handler = &CH_handler_config
    185     },
    186     {
    187       .url = "/setup/",
    188       .method = MHD_HTTP_METHOD_POST,
    189       .handler = &CH_handler_setup
    190     },
    191     {
    192       .url = "/authorize/",
    193       .method = MHD_HTTP_METHOD_GET,
    194       .handler = &CH_handler_authorize
    195     },
    196     {
    197       .url = "/authorize/",
    198       .method = MHD_HTTP_METHOD_POST,
    199       .handler = &CH_handler_authorize
    200     },
    201     {
    202       .url = "/challenge/",
    203       .method = MHD_HTTP_METHOD_POST,
    204       .handler = &CH_handler_challenge
    205     },
    206     {
    207       .url = "/solve/",
    208       .method = MHD_HTTP_METHOD_POST,
    209       .handler = &CH_handler_solve
    210     },
    211     {
    212       .url = "/token",
    213       .method = MHD_HTTP_METHOD_POST,
    214       .handler = &CH_handler_token
    215     },
    216     {
    217       .url = "/info",
    218       .method = MHD_HTTP_METHOD_GET,
    219       .handler = &CH_handler_info
    220     },
    221     {
    222       NULL, NULL, NULL
    223     }
    224   };
    225   struct CH_HandlerContext *hc = *con_cls;
    226 
    227   (void) cls;
    228   (void) version;
    229   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    230               "Handling %s request for `%s'\n",
    231               method,
    232               url);
    233   if (NULL == hc->connection)
    234   {
    235     const char *correlation_id;
    236     bool found = false;
    237 
    238     hc->connection = connection;
    239     /* We only read the correlation ID on the first callback for every client */
    240     correlation_id = MHD_lookup_connection_value (connection,
    241                                                   MHD_HEADER_KIND,
    242                                                   "Challenger-Correlation-Id");
    243     if ( (NULL != correlation_id) &&
    244          (GNUNET_YES != GNUNET_CURL_is_valid_scope_id (correlation_id)) )
    245     {
    246       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    247                   "Illegal incoming correlation ID\n");
    248       correlation_id = NULL;
    249     }
    250     if (NULL != correlation_id)
    251       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    252                   "Handling request for (%s) URL '%s', correlation_id=%s\n",
    253                   method,
    254                   url,
    255                   correlation_id);
    256     else
    257       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    258                   "Handling request (%s) for URL '%s'\n",
    259                   method,
    260                   url);
    261 
    262     for (unsigned int i = 0; NULL != handlers[i].url; i++)
    263     {
    264       struct CH_RequestHandler *rh = &handlers[i];
    265 
    266       if ( (0 == strcmp (url,
    267                          rh->url)) ||
    268            ( (0 == strncmp (url,
    269                             rh->url,
    270                             strlen (rh->url))) &&
    271              (1 < strlen (rh->url)) &&
    272              ('/' == rh->url[strlen (rh->url) - 1]) ) )
    273       {
    274         found = true;
    275         if (0 == strcasecmp (method,
    276                              MHD_HTTP_METHOD_OPTIONS))
    277         {
    278           return TALER_MHD_reply_cors_preflight (connection);
    279         }
    280         if (0 == strcasecmp (method,
    281                              rh->method))
    282         {
    283           hc->rh = rh;
    284           break;
    285         }
    286       }
    287     }
    288     if (NULL == hc->rh)
    289     {
    290       if (found)
    291       {
    292         char allow[128] = "OPTIONS";
    293         size_t aoff = strlen ("OPTIONS");
    294         struct MHD_Response *resp;
    295         enum MHD_Result ret;
    296 
    297         GNUNET_break_op (0);
    298         /* Build Allow header: OPTIONS is always supported (handled above),
    299            plus every method registered for this URL. */
    300         for (unsigned int j = 0; NULL != handlers[j].url; j++)
    301         {
    302           const struct CH_RequestHandler *rh2 = &handlers[j];
    303 
    304           if ( (0 != strcmp (url, rh2->url)) &&
    305                ! ( (0 == strncmp (url, rh2->url, strlen (rh2->url))) &&
    306                    (1 < strlen (rh2->url)) &&
    307                    ('/' == rh2->url[strlen (rh2->url) - 1]) ) )
    308             continue;
    309           GNUNET_assert (aoff + strlen (rh2->method) + 3 < sizeof (allow));
    310           memcpy (&allow[aoff], ", ", 2);
    311           aoff += 2;
    312           memcpy (&allow[aoff], rh2->method, strlen (rh2->method));
    313           aoff += strlen (rh2->method);
    314           allow[aoff] = '\0';
    315         }
    316 
    317         resp = MHD_create_response_from_buffer (0,
    318                                                 NULL,
    319                                                 MHD_RESPMEM_PERSISTENT);
    320         TALER_MHD_add_global_headers (resp,
    321                                       false);
    322         GNUNET_break (MHD_YES ==
    323                       MHD_add_response_header (resp,
    324                                                MHD_HTTP_HEADER_ALLOW,
    325                                                allow));
    326         ret = MHD_queue_response (hc->connection,
    327                                   MHD_HTTP_METHOD_NOT_ALLOWED,
    328                                   resp);
    329         MHD_destroy_response (resp);
    330         return ret;
    331       }
    332       GNUNET_break_op (0);
    333       return TALER_MHD_reply_static (hc->connection,
    334                                      MHD_HTTP_NOT_FOUND,
    335                                      "text/plain",
    336                                      NULL,
    337                                      0);
    338     }
    339   }
    340   else
    341   {
    342     GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    343   }
    344   GNUNET_assert (NULL != hc->rh);
    345   hc->path = &url[strlen (hc->rh->url)];
    346   return hc->rh->handler (hc,
    347                           upload_data,
    348                           upload_data_size);
    349 }
    350 
    351 
    352 /**
    353  * Shutdown task. Invoked when the application is being terminated.
    354  *
    355  * @param cls NULL
    356  */
    357 static void
    358 do_shutdown (void *cls)
    359 {
    360   (void) cls;
    361   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    362               "Stopping challenger-httpd\n");
    363   TALER_MHD_daemons_halt ();
    364   CH_wakeup_challenge_on_shutdown ();
    365   if ( (NULL != CH_message_template) &&
    366        (MAP_FAILED != CH_message_template) )
    367   {
    368     if (0 !=
    369         munmap (CH_message_template,
    370                 CH_message_template_len))
    371       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    372                            "munmap");
    373     CH_message_template = NULL;
    374     CH_message_template_len = 0;
    375   }
    376   TALER_MHD_daemons_destroy ();
    377   if (NULL != CH_ctx)
    378   {
    379     GNUNET_CURL_fini (CH_ctx);
    380     CH_ctx = NULL;
    381   }
    382   if (NULL != rc)
    383   {
    384     GNUNET_CURL_gnunet_rc_destroy (rc);
    385     rc = NULL;
    386   }
    387   if (NULL != CH_context)
    388   {
    389     CHALLENGERDB_disconnect (CH_context);
    390     CH_context = NULL;
    391   }
    392 }
    393 
    394 
    395 /**
    396  * Function called whenever MHD is done with a request.  If the
    397  * request was a POST, we may have stored a `struct Buffer *` in the
    398  * @a con_cls that might still need to be cleaned up.  Call the
    399  * respective function to free the memory.
    400  *
    401  * @param cls client-defined closure
    402  * @param connection connection handle
    403  * @param con_cls value as set by the last call to
    404  *        the #MHD_AccessHandlerCallback
    405  * @param toe reason for request termination
    406  * @see #MHD_OPTION_NOTIFY_COMPLETED
    407  * @ingroup request
    408  */
    409 static void
    410 handle_mhd_completion_callback (void *cls,
    411                                 struct MHD_Connection *connection,
    412                                 void **con_cls,
    413                                 enum MHD_RequestTerminationCode toe)
    414 {
    415   struct CH_HandlerContext *hc = *con_cls;
    416   const union MHD_ConnectionInfo *ci;
    417 
    418   (void) cls;
    419   if (NULL == hc)
    420     return;
    421   GNUNET_assert (hc->connection == connection);
    422   GNUNET_break (GNUNET_OK ==
    423                 CHALLENGERDB_preflight (CH_context));
    424   ci = MHD_get_connection_info (connection,
    425                                 MHD_CONNECTION_INFO_HTTP_STATUS);
    426   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    427               "Finished handling request with status %d (HTTP status %u)\n",
    428               (int) toe,
    429               (NULL != ci) ? ci->http_status : 0);
    430   if (NULL != hc->cc)
    431     hc->cc (hc->ctx);
    432   GNUNET_free (hc->full_url);
    433   GNUNET_free (hc);
    434   *con_cls = NULL;
    435 }
    436 
    437 
    438 /**
    439  * Kick MHD to run now, to be called after MHD_resume_connection().
    440  * Basically, we need to explicitly resume MHD's event loop whenever
    441  * we made progress serving a request.  This function re-schedules
    442  * the task processing MHD's activities to run immediately.
    443  *
    444  * FIXME: replace by direct call...
    445  */
    446 void
    447 CH_trigger_daemon ()
    448 {
    449   TALER_MHD_daemon_trigger ();
    450 }
    451 
    452 
    453 /**
    454  * Kick GNUnet Curl scheduler to begin curl interactions.
    455  */
    456 void
    457 CH_trigger_curl ()
    458 {
    459   GNUNET_CURL_gnunet_scheduler_reschedule (&rc);
    460 }
    461 
    462 
    463 /**
    464  * Callback invoked on every listen socket to start the
    465  * respective MHD HTTP daemon.
    466  *
    467  * @param cls unused
    468  * @param lsock the listen socket
    469  */
    470 static void
    471 start_daemon (void *cls,
    472               int lsock)
    473 {
    474   struct MHD_Daemon *mhd;
    475 
    476   (void) cls;
    477   GNUNET_assert (-1 != lsock);
    478   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK,
    479                           0 /* port */,
    480                           NULL, NULL,
    481                           &url_handler, NULL,
    482                           MHD_OPTION_LISTEN_SOCKET, lsock,
    483                           MHD_OPTION_URI_LOG_CALLBACK,
    484                           &full_url_track_callback, NULL,
    485                           MHD_OPTION_NOTIFY_COMPLETED,
    486                           &handle_mhd_completion_callback, NULL,
    487                           MHD_OPTION_CONNECTION_TIMEOUT,
    488                           (unsigned int) 10 /* 10s */,
    489                           MHD_OPTION_END);
    490   if (NULL == mhd)
    491   {
    492     global_ret = EXIT_NO_RESTART;
    493     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    494                 "Failed to launch HTTP daemon.\n");
    495     GNUNET_SCHEDULER_shutdown ();
    496     return;
    497   }
    498   have_daemons = true;
    499   TALER_MHD_daemon_start (mhd);
    500 }
    501 
    502 
    503 /**
    504  * Main function that will be run by the scheduler.
    505  *
    506  * @param cls closure
    507  * @param args remaining command-line arguments
    508  * @param cfgfile name of the configuration file used (for saving, can be
    509  *        NULL!)
    510  * @param config configuration
    511  */
    512 static void
    513 run (void *cls,
    514      char *const *args,
    515      const char *cfgfile,
    516      const struct GNUNET_CONFIGURATION_Handle *config)
    517 {
    518   enum TALER_MHD_GlobalOptions go;
    519 
    520   (void) cls;
    521   (void) args;
    522   (void) cfgfile;
    523   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    524               "Starting challenger-httpd\n");
    525   go = TALER_MHD_GO_NONE;
    526   if (CH_challenger_connection_close)
    527     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
    528   TALER_MHD_setup (go);
    529 
    530   if (GNUNET_OK !=
    531       CH_spa_init ())
    532   {
    533     global_ret = EXIT_NOTCONFIGURED;
    534     GNUNET_SCHEDULER_shutdown ();
    535     return;
    536   }
    537 
    538   if (GNUNET_OK !=
    539       GNUNET_CONFIGURATION_get_value_time (config,
    540                                            "CHALLENGER",
    541                                            "VALIDATION_DURATION",
    542                                            &CH_validation_duration))
    543   {
    544     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    545                                "CHALLENGER",
    546                                "VALIDATION_DURATION");
    547     global_ret = EXIT_NOTCONFIGURED;
    548     GNUNET_SCHEDULER_shutdown ();
    549     return;
    550   }
    551   if (GNUNET_OK !=
    552       GNUNET_CONFIGURATION_get_value_time (config,
    553                                            "CHALLENGER",
    554                                            "VALIDATION_EXPIRATION",
    555                                            &CH_validation_expiration))
    556   {
    557     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    558                                "CHALLENGER",
    559                                "VALIDATION_EXPIRATION");
    560     global_ret = EXIT_NOTCONFIGURED;
    561     GNUNET_SCHEDULER_shutdown ();
    562     return;
    563   }
    564   if (GNUNET_OK !=
    565       GNUNET_CONFIGURATION_get_value_time (config,
    566                                            "CHALLENGER",
    567                                            "TOKEN_EXPIRATION",
    568                                            &CH_token_expiration))
    569   {
    570     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    571                                "CHALLENGER",
    572                                "VALIDATION_EXPIRATION");
    573     CH_token_expiration = GNUNET_TIME_UNIT_HOURS;
    574   }
    575   if (GNUNET_OK !=
    576       GNUNET_CONFIGURATION_get_value_string (config,
    577                                              "CHALLENGER",
    578                                              "AUTH_COMMAND",
    579                                              &CH_auth_command))
    580   {
    581     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    582                                "CHALLENGER",
    583                                "AUTH_COMMAND");
    584     global_ret = EXIT_NOTCONFIGURED;
    585     GNUNET_SCHEDULER_shutdown ();
    586     return;
    587   }
    588   if (GNUNET_OK !=
    589       GNUNET_CONFIGURATION_get_value_string (config,
    590                                              "CHALLENGER",
    591                                              "ADDRESS_TYPE",
    592                                              &CH_address_type))
    593   {
    594     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    595                                "CHALLENGER",
    596                                "ADDRESS_TYPE");
    597     global_ret = EXIT_NOTCONFIGURED;
    598     GNUNET_SCHEDULER_shutdown ();
    599     return;
    600   }
    601   if (GNUNET_OK !=
    602       GNUNET_CONFIGURATION_get_value_string (config,
    603                                              "CHALLENGER",
    604                                              "ADDRESS_HINT",
    605                                              &CH_address_hint))
    606   {
    607     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    608                                "CHALLENGER",
    609                                "ADDRESS_HINT");
    610     CH_address_hint = GNUNET_strdup ("");
    611   }
    612   if (GNUNET_OK !=
    613       GNUNET_CONFIGURATION_get_value_string (config,
    614                                              "CHALLENGER",
    615                                              "BASE_URL",
    616                                              &CH_base_url))
    617   {
    618     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    619                                "CHALLENGER",
    620                                "BASE_URL");
    621     global_ret = EXIT_NOTCONFIGURED;
    622     GNUNET_SCHEDULER_shutdown ();
    623     return;
    624   }
    625   if ( (! TALER_url_valid_charset (CH_base_url)) ||
    626        (! TALER_is_web_url (CH_base_url) ) ||
    627        (0 == strlen (CH_base_url)) ||
    628        ('/' != CH_base_url[strlen (CH_base_url) - 1]) )
    629   {
    630     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    631                                "CHALLENGER",
    632                                "BASE_URL",
    633                                "invalid URL");
    634     global_ret = EXIT_NOTCONFIGURED;
    635     GNUNET_SCHEDULER_shutdown ();
    636     return;
    637   }
    638   {
    639     char *tmpl_file;
    640     int fd;
    641     struct stat s;
    642 
    643     if (GNUNET_OK !=
    644         GNUNET_CONFIGURATION_get_value_filename (config,
    645                                                  "CHALLENGER",
    646                                                  "MESSAGE_TEMPLATE_FILE",
    647                                                  &tmpl_file))
    648     {
    649       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    650                                  "CHALLENGER",
    651                                  "MESSAGE_TEMPLATE_FILE");
    652     }
    653     else
    654     {
    655       fd = open (tmpl_file,
    656                  O_RDONLY);
    657       GNUNET_free (tmpl_file);
    658       if (-1 == fd)
    659       {
    660         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    661                                    "CHALLENGER",
    662                                    "MESSAGE_TEMPLATE_FILE",
    663                                    strerror (errno));
    664         global_ret = EXIT_NOTCONFIGURED;
    665         GNUNET_SCHEDULER_shutdown ();
    666         return;
    667       }
    668       if (0 != fstat (fd,
    669                       &s))
    670       {
    671         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    672                                    "CHALLENGER",
    673                                    "MESSAGE_TEMPLATE_FILE",
    674                                    strerror (errno));
    675         GNUNET_free (tmpl_file);
    676         GNUNET_break (0 == close (fd));
    677         global_ret = EXIT_NOTCONFIGURED;
    678         GNUNET_SCHEDULER_shutdown ();
    679         return;
    680       }
    681       CH_message_template_len = (size_t) s.st_size;
    682       CH_message_template = mmap (NULL,
    683                                   CH_message_template_len,
    684                                   PROT_READ,
    685                                   MAP_SHARED,
    686                                   fd,
    687                                   0);
    688       GNUNET_break (0 == close (fd));
    689       if (MAP_FAILED == CH_message_template)
    690       {
    691         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    692                                    "CHALLENGER",
    693                                    "MESSAGE_TEMPLATE_FILE",
    694                                    strerror (errno));
    695         GNUNET_free (tmpl_file);
    696         global_ret = EXIT_NOTCONFIGURED;
    697         GNUNET_SCHEDULER_shutdown ();
    698         return;
    699       }
    700     }
    701   }
    702 
    703   {
    704     char *restrictions;
    705 
    706     if (GNUNET_OK ==
    707         GNUNET_CONFIGURATION_get_value_string (config,
    708                                                "CHALLENGER",
    709                                                "ADDRESS_RESTRICTIONS",
    710                                                &restrictions))
    711     {
    712       json_error_t err;
    713 
    714       CH_restrictions = json_loads (restrictions,
    715                                     JSON_REJECT_DUPLICATES,
    716                                     &err);
    717       GNUNET_free (restrictions);
    718       if (NULL == CH_restrictions)
    719       {
    720         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    721                                    "CHALLENGER",
    722                                    "ADDRESS_RESTRICTIONS",
    723                                    err.text);
    724         global_ret = EXIT_NOTCONFIGURED;
    725         GNUNET_SCHEDULER_shutdown ();
    726         return;
    727       }
    728     }
    729     else
    730     {
    731       CH_restrictions = json_object ();
    732       GNUNET_assert (NULL != CH_restrictions);
    733     }
    734   }
    735 
    736   global_ret = EXIT_NOTCONFIGURED;
    737   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    738                                  NULL);
    739   /* setup HTTP client event loop */
    740   CH_ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    741                              &rc);
    742   rc = GNUNET_CURL_gnunet_rc_create (CH_ctx);
    743   if (NULL ==
    744       (CH_context = CHALLENGERDB_connect (config,
    745                                           false)))
    746   {
    747     global_ret = EXIT_NOTINSTALLED;
    748     GNUNET_SCHEDULER_shutdown ();
    749     return;
    750   }
    751   if (GNUNET_OK !=
    752       CHALLENGERDB_preflight (CH_context))
    753   {
    754     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    755                 "Database not setup. Did you run challenger-dbinit?\n");
    756     GNUNET_SCHEDULER_shutdown ();
    757     return;
    758   }
    759   {
    760     enum GNUNET_GenericReturnValue ret;
    761 
    762     ret = TALER_MHD_listen_bind (config,
    763                                  "challenger",
    764                                  &start_daemon,
    765                                  NULL);
    766     switch (ret)
    767     {
    768     case GNUNET_SYSERR:
    769       global_ret = EXIT_NOTCONFIGURED;
    770       GNUNET_SCHEDULER_shutdown ();
    771       return;
    772     case GNUNET_NO:
    773       if (! have_daemons)
    774       {
    775         global_ret = EXIT_NOTCONFIGURED;
    776         GNUNET_SCHEDULER_shutdown ();
    777         return;
    778       }
    779       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    780                   "Could not open all configured listen sockets\n");
    781       break;
    782     case GNUNET_OK:
    783       break;
    784     }
    785   }
    786   global_ret = EXIT_SUCCESS;
    787   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    788               "Challenger-httpd ready\n");
    789 }
    790 
    791 
    792 /**
    793  * The main function of the serve tool
    794  *
    795  * @param argc number of arguments from the command line
    796  * @param argv command line arguments
    797  * @return 0 ok, 1 on error
    798  */
    799 int
    800 main (int argc,
    801       char *const *argv)
    802 {
    803   struct GNUNET_GETOPT_CommandLineOption options[] = {
    804     GNUNET_GETOPT_option_flag ('C',
    805                                "connection-close",
    806                                "force HTTP connections to be closed after each request",
    807                                &CH_challenger_connection_close),
    808     GNUNET_GETOPT_OPTION_END
    809   };
    810   enum GNUNET_GenericReturnValue ret;
    811 
    812   ret = GNUNET_PROGRAM_run (CHALLENGER_project_data (),
    813                             argc, argv,
    814                             "challenger-httpd",
    815                             "challenger REST and OAuth 2.0 API",
    816                             options,
    817                             &run, NULL);
    818   if (GNUNET_NO == ret)
    819     return EXIT_SUCCESS;
    820   if (GNUNET_SYSERR == ret)
    821     return EXIT_INVALIDARGUMENT;
    822   return global_ret;
    823 }