twister

HTTP fault injector for testing
Log | Files | Refs | README | LICENSE

taler-twister-service.c (78258B)


      1 /*
      2   This file is part of GNUnet.
      3   Copyright (C) 2012-2014 GNUnet e.V.
      4   Copyright (C) 2018 Taler Systems SA
      5 
      6   GNUnet is free software; you can redistribute it and/or
      7   modify it under the terms of the GNU General Public License
      8   as published by the Free Software Foundation; either version
      9   3, or (at your option) any later version.
     10 
     11   GNUnet is distributed in the hope that it will be useful, but
     12   WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14   GNU General Public License for more details.
     15 
     16   You should have received a copy of the GNU General Public
     17   License along with GNUnet; see the file COPYING.  If not,
     18   write to the Free Software Foundation, Inc., 51 Franklin
     19   Street, Fifth Floor, Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @author Martin Schanzenbach
     24  * @author Christian Grothoff
     25  * @author Marcello Stanisci
     26  * @file src/twister/taler-twister-service.c
     27  * @brief HTTP proxy that acts as a man in the
     28  *        middle making changes to requests or responses
     29  */
     30 #include "platform.h"
     31 #include <microhttpd.h>
     32 #include <curl/curl.h>
     33 #include <gnunet/gnunet_util_lib.h>
     34 #include <gnunet/gnunet_json_lib.h>
     35 #include <gnunet/gnunet_mhd_compat.h>
     36 #include <gnunet/gnunet_mhd_lib.h>
     37 #include "twister.h"
     38 #include "taler_twister_service.h"
     39 #include <jansson.h>
     40 #include <microhttpd.h>
     41 #include <zlib.h>
     42 
     43 
     44 #define TWISTER_LOG_INFO(...)                                  \
     45         GNUNET_log (GNUNET_ERROR_TYPE_INFO, __VA_ARGS__)
     46 #define TWISTER_LOG_DEBUG(...)                                  \
     47         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, __VA_ARGS__)
     48 #define TWISTER_LOG_WARNING(...)                                  \
     49         GNUNET_log (GNUNET_ERROR_TYPE_WARNING, __VA_ARGS__)
     50 #define TWISTER_LOG_ERROR(...)                                  \
     51         GNUNET_log (GNUNET_ERROR_TYPE_ERROR, __VA_ARGS__)
     52 
     53 #define REQUEST_BUFFER_MAX (1024 * 1024)
     54 #define UNIX_BACKLOG 500
     55 
     56 /**
     57  * Log curl error.
     58  *
     59  * @param level log level
     60  * @param fun name of curl_easy-function that gave the error
     61  * @param rc return code from curl
     62  */
     63 #define LOG_CURL_EASY(level,fun,rc) \
     64         GNUNET_log (level, _ ("%s failed at %s:%d: `%s'\n"), fun, __FILE__, \
     65                     __LINE__, \
     66                     curl_easy_strerror (rc))
     67 
     68 /* ******** Datastructures for HTTP handling ********** */
     69 
     70 
     71 /**
     72  * State machine for HTTP requests (per request).
     73  */
     74 enum RequestState
     75 {
     76   /**
     77    * Starting state.
     78    */
     79   REQUEST_STATE_WITH_MHD = 0,
     80 
     81   /**
     82    * We've started receiving upload data from MHD.
     83    */
     84   REQUEST_STATE_CLIENT_UPLOAD_STARTED,
     85 
     86   /**
     87    * Wa have started uploading data to the proxied service.
     88    */
     89   REQUEST_STATE_PROXY_UPLOAD_STARTED,
     90 
     91   /**
     92    * We're done with the upload from MHD.
     93    */
     94   REQUEST_STATE_CLIENT_UPLOAD_DONE,
     95 
     96   /**
     97    * We're done uploading data to the proxied service.
     98    */
     99   REQUEST_STATE_PROXY_UPLOAD_DONE,
    100 
    101   /**
    102    * We've finished uploading data via CURL and can now download.
    103    */
    104   REQUEST_STATE_PROXY_DOWNLOAD_STARTED,
    105 
    106   /**
    107    * We've finished receiving download data from cURL.
    108    */
    109   REQUEST_STATE_PROXY_DOWNLOAD_DONE
    110 };
    111 
    112 
    113 /**
    114  * A header list
    115  */
    116 struct HttpResponseHeader
    117 {
    118   /**
    119    * DLL
    120    */
    121   struct HttpResponseHeader *next;
    122 
    123   /**
    124    * DLL
    125    */
    126   struct HttpResponseHeader *prev;
    127 
    128   /**
    129    * Header type
    130    */
    131   char *type;
    132 
    133   /**
    134    * Header value
    135    */
    136   char *value;
    137 };
    138 
    139 
    140 /**
    141  * A structure for socks requests
    142  */
    143 struct HttpRequest
    144 {
    145 
    146   /**
    147    * Kept in DLL.
    148    */
    149   struct HttpRequest *prev;
    150 
    151   /**
    152    * Kept in DLL.
    153    */
    154   struct HttpRequest *next;
    155 
    156   /**
    157    * MHD request that triggered us.
    158    */
    159   struct MHD_Connection *con;
    160 
    161   /**
    162    * Client socket read task
    163    */
    164   struct GNUNET_SCHEDULER_Task *rtask;
    165 
    166   /**
    167    * Client socket write task
    168    */
    169   struct GNUNET_SCHEDULER_Task *wtask;
    170 
    171   /**
    172    * Hold the response obtained by modifying the original one.
    173    */
    174   struct MHD_Response *mod_response;
    175 
    176   /**
    177    * MHD response object for this request.
    178    */
    179   struct MHD_Response *response;
    180 
    181   /**
    182    * The URL to fetch
    183    */
    184   char *url;
    185 
    186   /**
    187    * JSON we use to parse payloads (in both directions).
    188    */
    189   json_t *json;
    190 
    191   /**
    192    * Handle to cURL
    193    */
    194   CURL *curl;
    195 
    196   /**
    197    * HTTP request headers for the curl request.
    198    */
    199   struct curl_slist *headers;
    200 
    201   /**
    202    * Headers from response
    203    */
    204   struct HttpResponseHeader *header_head;
    205 
    206   /**
    207    * Headers from response
    208    */
    209   struct HttpResponseHeader *header_tail;
    210 
    211   /**
    212    * Buffer we use for moving data between MHD and
    213    * curl (in both directions).
    214    */
    215   char *io_buf;
    216 
    217   /**
    218    * Number of bytes already in the IO buffer.
    219    */
    220   size_t io_len;
    221 
    222   /**
    223    * Number of bytes allocated for the IO buffer.
    224    */
    225   unsigned int io_size;
    226 
    227   /**
    228    * HTTP response code to give to MHD for the response.
    229    */
    230   unsigned int response_code;
    231 
    232   /**
    233    * Request processing state machine.
    234    */
    235   enum RequestState state;
    236 
    237   /**
    238    * Did we suspend MHD processing?
    239    */
    240   enum GNUNET_GenericReturnValue suspended;
    241 
    242   /**
    243    * Did we pause CURL processing?
    244    */
    245   int curl_paused;
    246 };
    247 
    248 
    249 /* *********************** Globals **************************** */
    250 
    251 /**
    252  * The cURL download task (curl multi API).
    253  */
    254 static struct GNUNET_SCHEDULER_Task *curl_download_task;
    255 
    256 /**
    257  * DLL of active HTTP requests.
    258  */
    259 static struct HttpRequest *hr_head;
    260 
    261 /**
    262  * DLL of active HTTP requests.
    263  */
    264 static struct HttpRequest *hr_tail;
    265 
    266 /**
    267  * The cURL multi handle
    268  */
    269 static CURLM *curl_multi;
    270 
    271 /**
    272  * The daemon handle
    273  */
    274 static struct MHD_Daemon *mhd_daemon;
    275 
    276 /**
    277  * The task ID
    278  */
    279 static struct GNUNET_SCHEDULER_Task *httpd_task;
    280 
    281 /**
    282  * Response we return on cURL failures.
    283  */
    284 static struct MHD_Response *curl_failure_response;
    285 
    286 /**
    287  * Our configuration.
    288  */
    289 static const struct GNUNET_CONFIGURATION_Handle *cfg;
    290 
    291 /**
    292  * Destination to which HTTP server we forward requests to.
    293  * Of the format "http://servername:PORT"
    294  */
    295 static char *target_server_base_url;
    296 
    297 /* ******************* Transformations ***************** */
    298 
    299 /**
    300  * Chaos probability (in percent); this value is taken from
    301  * the config and stays valid for all the Twister's lifetime.
    302  */
    303 static long long unsigned int chaos_rate = 0;
    304 
    305 
    306 /**
    307  * Set to non-zero if we should change the next response code.
    308  * In this case, this is the value to use.
    309  */
    310 static unsigned int hack_response_code;
    311 
    312 /**
    313  * Will point to a JSON object to delete.  Only cares about
    314  * _download_ objects.
    315  */
    316 static char *delete_path;
    317 
    318 /**
    319  * Will point to a JSON _string_ object
    320  * which will get a character flipped.
    321  * Only checked against _download_ objects.
    322  */
    323 static char *flip_path_dl;
    324 
    325 /**
    326  * Will point to a JSON _string_ object
    327  * which will get a character flipped.
    328  * Only checked against _upload_ objects.
    329  */
    330 static char *flip_path_ul;
    331 
    332 /**
    333  * Will point to a JSON object to modify.
    334  * Only checked against _download_ objects.
    335  */
    336 static char *modify_path_dl;
    337 
    338 /**
    339  * Will point to a JSON object to modify.
    340  * Only checked against _upload_ objects.
    341  */
    342 static char *modify_path_ul;
    343 
    344 
    345 /**
    346  * If not NULL, the name of the header value to
    347  * modify in the response.
    348  */
    349 static char *modify_header_dl;
    350 
    351 
    352 /**
    353  * If true, will randomly truncate the request body
    354  * to upload to the proxied service.
    355  */
    356 static unsigned int malform_upload;
    357 
    358 /**
    359  * If true, will randomly truncate the response body
    360  * before returning to the client.
    361  */
    362 static unsigned int malform;
    363 
    364 /**
    365  * New value to give the modified field.
    366  * Both for upload and download mods.
    367  */
    368 static char *modify_value;
    369 
    370 /**
    371  * Size of the malformed body to be uploaded to the
    372  * proxied service.
    373  */
    374 static size_t malformed_size;
    375 
    376 /* ********************* Global helpers ****************** */
    377 
    378 /**
    379  * Run MHD now, we have extra data ready for the callback.
    380  */
    381 static void
    382 run_mhd_now (void);
    383 
    384 
    385 /* *************** HTTP handling with cURL ***************** */
    386 
    387 
    388 /**
    389  * Transform _one_ CURL header (gotten from the request) into
    390  * MHD format and put it into the response headers list; mostly
    391  * copies the headers, but makes special adjustments based on
    392  * control requests.
    393  *
    394  * @param buffer curl buffer with a single
    395  *        line of header data; not 0-terminated!
    396  * @param size curl blocksize
    397  * @param nmemb curl blocknumber
    398  * @param cls our `struct HttpRequest *`
    399  * @return size of processed bytes
    400  */
    401 static size_t
    402 curl_check_hdr (void *buffer,
    403                 size_t size,
    404                 size_t nmemb,
    405                 void *cls)
    406 {
    407   struct HttpRequest *hr = cls;
    408   struct HttpResponseHeader *header;
    409   size_t bytes = size * nmemb;
    410   char *ndup;
    411   const char *hdr_type;
    412   char *hdr_val;
    413   char *tok;
    414 
    415   /* Raw line is not guaranteed to be null-terminated.  */
    416   ndup = GNUNET_malloc (bytes + 1);
    417   memcpy (ndup,
    418           buffer,
    419           bytes);
    420   ndup[bytes] = '\0';
    421 
    422   hdr_type = strtok (ndup, ":");
    423 
    424   if (NULL == hdr_type)
    425   {
    426     GNUNET_free (ndup);
    427     return bytes;
    428   }
    429   hdr_val = strtok (NULL, "");
    430   if (NULL == hdr_val)
    431   {
    432     GNUNET_free (ndup);
    433     return bytes;
    434   }
    435   if (' ' == *hdr_val)
    436     hdr_val++;
    437 
    438   /* MHD does not allow certain characters in values,
    439    * remove those, plus those could alter strings matching.  */
    440   if (NULL != (tok = strchr (hdr_val, '\n')))
    441     *tok = '\0';
    442   if (NULL != (tok = strchr (hdr_val, '\r')))
    443     *tok = '\0';
    444   if (NULL != (tok = strchr (hdr_val, '\t')))
    445     *tok = '\0';
    446 
    447   TWISTER_LOG_DEBUG ("Parsed line: '%s: %s'\n",
    448                      hdr_type,
    449                      hdr_val);
    450 
    451   /* Skip "Content-length:" header as it will be wrong, given
    452      that we are man-in-the-middling the connection */
    453   if (0 == strcasecmp (hdr_type,
    454                        MHD_HTTP_HEADER_CONTENT_LENGTH))
    455   {
    456     GNUNET_free (ndup);
    457     return bytes;
    458   }
    459   /* Skip "Connection: Keep-Alive" header, it will be
    460      done by MHD if possible */
    461   if ( (0 == strcasecmp (hdr_type,
    462                          MHD_HTTP_HEADER_CONNECTION)) &&
    463        (0 == strcasecmp (hdr_val,
    464                          "Keep-Alive")) )
    465   {
    466     GNUNET_free (ndup);
    467     return bytes;
    468   }
    469   if (0 != strlen (hdr_val)) /* Rely in MHD to set those */
    470   {
    471     header = GNUNET_new (struct HttpResponseHeader);
    472     header->type = GNUNET_strdup (hdr_type);
    473     header->value = GNUNET_strdup (hdr_val);
    474     GNUNET_CONTAINER_DLL_insert (hr->header_head,
    475                                  hr->header_tail,
    476                                  header);
    477   }
    478   GNUNET_free (ndup);
    479   return bytes;
    480 }
    481 
    482 
    483 /**
    484  * Create the MHD response with CURL's as starting base;
    485  * mainly set the response code and parses the response into
    486  * JSON, if it is such.
    487  *
    488  * @param hr pointer to where to store the new data.  Despite
    489  *        its name, the struct contains response data as well.
    490  * @return #GNUNET_OK if it succeeds.
    491  */
    492 static enum GNUNET_GenericReturnValue
    493 create_mhd_response_from_hr (struct HttpRequest *hr)
    494 {
    495   long resp_code;
    496   json_error_t error;
    497 
    498   if (NULL != hr->response)
    499   {
    500     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    501                 "Response already set!\n");
    502     return GNUNET_SYSERR;
    503   }
    504   GNUNET_break (CURLE_OK ==
    505                 curl_easy_getinfo (hr->curl,
    506                                    CURLINFO_RESPONSE_CODE,
    507                                    &resp_code));
    508   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    509               "Creating MHD response with code %u\n",
    510               (unsigned int) resp_code);
    511   hr->response_code = resp_code;
    512   /* Note, will be NULL if io_buf does not represent
    513    * a JSON value.  */
    514   hr->json = json_loadb (hr->io_buf,
    515                          hr->io_len,
    516                          JSON_DECODE_ANY,
    517                          &error);
    518   if (GNUNET_YES == hr->suspended)
    519   {
    520     MHD_resume_connection (hr->con);
    521     hr->suspended = GNUNET_NO;
    522   }
    523   run_mhd_now ();
    524   return GNUNET_OK;
    525 }
    526 
    527 
    528 /**
    529  * Handle response payload data from cURL.
    530  * Copies it into our `io_buf` to make it available to MHD.
    531  *
    532  * @param ptr pointer to the data
    533  * @param size number of blocks of data
    534  * @param nmemb blocksize
    535  * @param ctx our `struct HttpRequest *`
    536  * @return number of bytes handled
    537  */
    538 static size_t
    539 curl_download_cb (void *ptr,
    540                   size_t size,
    541                   size_t nmemb,
    542                   void *ctx)
    543 {
    544   struct HttpRequest *hr = ctx;
    545   size_t total = size * nmemb;
    546 
    547   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    548               "Curl download proceeding\n");
    549 
    550   if (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state)
    551   {
    552     /* Web server started with response before we finished
    553        the upload.  In this case, current libcurl decides
    554        to NOT complete the upload, so we should jump in the
    555        state machine to process the download, dropping the
    556        rest of the upload.  This should only really happen
    557        with uploads without "Expect: 100 Continue" and
    558        Web servers responding with an error (i.e. upload
    559        not allowed) */
    560     hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
    561     GNUNET_log
    562       (GNUNET_ERROR_TYPE_INFO,
    563       "Stopping %u byte upload: we are already downloading...\n",
    564       (unsigned int) hr->io_len);
    565     hr->io_len = 0;
    566   }
    567 
    568   if (REQUEST_STATE_PROXY_DOWNLOAD_STARTED != hr->state)
    569   {
    570     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    571                 "Download callback goes to sleep\n");
    572     hr->curl_paused = GNUNET_YES;
    573     return CURL_WRITEFUNC_PAUSE;
    574   }
    575   GNUNET_assert (REQUEST_STATE_PROXY_DOWNLOAD_STARTED ==
    576                  hr->state);
    577   if (hr->io_size - hr->io_len < total)
    578   {
    579     GNUNET_assert (total + hr->io_size >= total);
    580     GNUNET_assert (hr->io_size * 2 + 1024 > hr->io_size);
    581     GNUNET_array_grow (hr->io_buf,
    582                        hr->io_size,
    583                        GNUNET_MAX (total + hr->io_len,
    584                                    hr->io_size * 2 + 1024));
    585   }
    586   GNUNET_memcpy (&hr->io_buf[hr->io_len],
    587                  ptr,
    588                  total);
    589   hr->io_len += total;
    590   return total;
    591 }
    592 
    593 
    594 /**
    595  * Ask cURL for the select() sets and schedule cURL operations.
    596  */
    597 static void
    598 curl_download_prepare (void);
    599 
    600 
    601 /**
    602  * cURL callback for uploaded (PUT/POST) data.
    603  * Copies from our `io_buf` to make it available to cURL.
    604  *
    605  * @param buf where to write the data
    606  * @param size number of bytes per member
    607  * @param nmemb number of members available in @a buf
    608  * @param cls our `struct HttpRequest` that generated the data
    609  * @return number of bytes copied to @a buf
    610  */
    611 static size_t
    612 curl_upload_cb (void *buf,
    613                 size_t size,
    614                 size_t nmemb,
    615                 void *cls)
    616 {
    617   struct HttpRequest *hr = cls;
    618   size_t len = size * nmemb;
    619   size_t to_copy;
    620 
    621   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    622               "Upload cb is working...\n");
    623 
    624   if ( (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state) ||
    625        (REQUEST_STATE_PROXY_DOWNLOAD_DONE == hr->state) )
    626   {
    627     GNUNET_log
    628       (GNUNET_ERROR_TYPE_INFO,
    629       "Upload cb aborts: we are already downloading...\n");
    630     return CURL_READFUNC_ABORT;
    631   }
    632 
    633   if ( (0 == hr->io_len) &&
    634        (REQUEST_STATE_PROXY_UPLOAD_STARTED == hr->state) )
    635   {
    636     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    637                 "Pausing CURL UPLOAD, need more data\n");
    638     return CURL_READFUNC_PAUSE;
    639   }
    640 
    641   /**
    642    * We got rescheduled because the download callback was asleep.
    643    * FIXME: can this block be eliminated and the unpausing being
    644    * moved in the last block where we return zero as well?
    645    */
    646   if ( (0 == hr->io_len) &&
    647        (REQUEST_STATE_PROXY_DOWNLOAD_STARTED == hr->state) )
    648   {
    649     if (GNUNET_YES == hr->curl_paused)
    650     {
    651       hr->curl_paused = GNUNET_NO;
    652       curl_easy_pause (hr->curl,
    653                        CURLPAUSE_CONT);
    654     }
    655     curl_download_prepare ();
    656     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    657                 "Completed CURL UPLOAD\n");
    658     return 0; /* upload finished, can now download */
    659   }
    660   to_copy = GNUNET_MIN (hr->io_len,
    661                         len);
    662   GNUNET_memcpy (buf,
    663                  hr->io_buf,
    664                  to_copy);
    665   /* shift remaining data back to the beginning of the buffer.  */
    666   memmove (hr->io_buf,
    667            &hr->io_buf[to_copy],
    668            hr->io_len - to_copy);
    669   hr->io_len -= to_copy;
    670   if (0 == hr->io_len)
    671   {
    672     hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
    673     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    674                 "Completed CURL UPLOAD\n");
    675   }
    676   return to_copy;
    677 }
    678 
    679 
    680 /* ************** helper functions ************* */
    681 
    682 /**
    683  * Extract the hostname from a complete URL.
    684  *
    685  * @param url full fledged URL
    686  *
    687  * @return pointer to the 0-terminated hostname, to be freed
    688  *         by the caller.
    689  */
    690 static char *
    691 build_host_header (const char *url)
    692 {
    693   #define MARKER "://"
    694 
    695   char *header;
    696   char *end;
    697   char *hostname;
    698   char *dup = GNUNET_strdup (url);
    699 
    700   hostname = strstr (dup, MARKER);
    701   hostname += 3;
    702 
    703   end = strchrnul (hostname, '/');
    704   *end = '\0';
    705 
    706   GNUNET_asprintf (&header,
    707                    "Host: %s",
    708                    hostname);
    709 
    710   GNUNET_free (dup);
    711   return header;
    712 }
    713 
    714 
    715 /* ************** main loop of cURL interaction ************* */
    716 
    717 
    718 /**
    719  * Task that is run when we are ready to receive more data
    720  * from curl
    721  *
    722  * @param cls closure
    723  */
    724 static void
    725 curl_task_download (void *cls);
    726 
    727 
    728 /**
    729  * Ask cURL for the select() sets and schedule cURL operations.
    730  */
    731 static void
    732 curl_download_prepare ()
    733 {
    734   CURLMcode mret;
    735   fd_set rs;
    736   fd_set ws;
    737   fd_set es;
    738   int max;
    739   struct GNUNET_NETWORK_FDSet *grs;
    740   struct GNUNET_NETWORK_FDSet *gws;
    741   long to;
    742   struct GNUNET_TIME_Relative rtime;
    743 
    744   if (NULL != curl_download_task)
    745   {
    746     GNUNET_SCHEDULER_cancel (curl_download_task);
    747     curl_download_task = NULL;
    748   }
    749   max = -1;
    750   FD_ZERO (&rs);
    751   FD_ZERO (&ws);
    752   FD_ZERO (&es);
    753   if (CURLM_OK != (mret = curl_multi_fdset (curl_multi,
    754                                             &rs,
    755                                             &ws,
    756                                             &es,
    757                                             &max)))
    758   {
    759     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    760                 "%s failed at %s:%d: `%s'\n",
    761                 "curl_multi_fdset",
    762                 __FILE__,
    763                 __LINE__,
    764                 curl_multi_strerror (mret));
    765     return;
    766   }
    767   to = -1;
    768   GNUNET_break (CURLM_OK ==
    769                 curl_multi_timeout (curl_multi,
    770                                     &to));
    771   if (-1 == to)
    772     rtime = GNUNET_TIME_UNIT_FOREVER_REL;
    773   else
    774     rtime = GNUNET_TIME_relative_multiply
    775               (GNUNET_TIME_UNIT_MILLISECONDS, to);
    776   if (-1 != max)
    777   {
    778     grs = GNUNET_NETWORK_fdset_create ();
    779     gws = GNUNET_NETWORK_fdset_create ();
    780     GNUNET_NETWORK_fdset_copy_native (grs,
    781                                       &rs,
    782                                       max + 1);
    783     GNUNET_NETWORK_fdset_copy_native (gws,
    784                                       &ws,
    785                                       max + 1);
    786     curl_download_task
    787       = GNUNET_SCHEDULER_add_select (
    788           GNUNET_SCHEDULER_PRIORITY_DEFAULT,
    789           rtime,
    790           grs, gws,
    791           &curl_task_download,
    792           curl_multi);
    793     GNUNET_NETWORK_fdset_destroy (gws);
    794     GNUNET_NETWORK_fdset_destroy (grs);
    795   }
    796   else
    797   {
    798     curl_download_task = GNUNET_SCHEDULER_add_delayed
    799                            (rtime,
    800                            &curl_task_download,
    801                            curl_multi);
    802   }
    803 }
    804 
    805 
    806 /**
    807  * Task that is run when we are ready to receive
    808  * more data from curl.
    809  *
    810  * @param cls closure, usually NULL.
    811  */
    812 static void
    813 curl_task_download (void *cls)
    814 {
    815   int running;
    816   int msgnum;
    817   struct CURLMsg *msg;
    818   CURLMcode mret;
    819   struct HttpRequest *hr;
    820 
    821   (void) cls;
    822   curl_download_task = NULL;
    823   do
    824   {
    825     running = 0;
    826     mret = curl_multi_perform (curl_multi,
    827                                &running);
    828     while (NULL != (msg = curl_multi_info_read (curl_multi,
    829                                                 &msgnum)))
    830     {
    831       GNUNET_break
    832         (CURLE_OK == curl_easy_getinfo
    833           (msg->easy_handle,
    834           CURLINFO_PRIVATE,
    835           (char **) &hr));
    836 
    837       if (NULL == hr)
    838       {
    839         GNUNET_break (0);
    840         continue;
    841       }
    842       switch (msg->msg)
    843       {
    844       case CURLMSG_NONE:
    845         /* documentation says this is not used */
    846         GNUNET_break (0);
    847         break;
    848       case CURLMSG_DONE:
    849         switch (msg->data.result)
    850         {
    851         case CURLE_OK:
    852         case CURLE_GOT_NOTHING:
    853           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    854                       "CURL download completed.\n");
    855           hr->state = REQUEST_STATE_PROXY_DOWNLOAD_DONE;
    856           if (NULL == hr->response)
    857             GNUNET_assert (GNUNET_OK ==
    858                            create_mhd_response_from_hr (hr));
    859           break;
    860         default:
    861           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    862                       "Download curl failed: %s\n",
    863                       curl_easy_strerror (msg->data.result));
    864           /* FIXME: indicate error somehow?
    865            * close MHD connection badly as well? */
    866           hr->state = REQUEST_STATE_PROXY_DOWNLOAD_DONE;
    867           if (GNUNET_YES == hr->suspended)
    868           {
    869             MHD_resume_connection (hr->con);
    870             hr->suspended = GNUNET_NO;
    871           }
    872           run_mhd_now ();
    873           break;
    874         }
    875         if (NULL == hr->response)
    876           hr->response = curl_failure_response;
    877         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    878                     "Curl request for `%s' finished (got the response)\n",
    879                     hr->url);
    880         run_mhd_now ();
    881         break;
    882       case CURLMSG_LAST:
    883         /* documentation says this is not used */
    884         GNUNET_break (0);
    885         break;
    886       default:
    887         /* unexpected status code */
    888         GNUNET_break (0);
    889         break;
    890       }
    891     }
    892     ;
    893   } while (mret == CURLM_CALL_MULTI_PERFORM);
    894   if (CURLM_OK != mret)
    895     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    896                 "%s failed at %s:%d: `%s'\n",
    897                 "curl_multi_perform",
    898                 __FILE__,
    899                 __LINE__,
    900                 curl_multi_strerror (mret));
    901   if (0 == running)
    902   {
    903     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    904                 "Suspending cURL multi loop,"
    905                 " no more events pending\n");
    906     return; /* nothing more in progress */
    907   }
    908   curl_download_prepare ();
    909 }
    910 
    911 
    912 /* *************** MHD response generation ***************** */
    913 
    914 
    915 /**
    916  * "Filter" function that translates MHD request headers to
    917  * cURL's.
    918  *
    919  * @param cls our `struct HttpRequest`
    920  * @param kind value kind
    921  * @param key field key
    922  * @param value field value
    923  * @return #MHD_YES to continue to iterate
    924  */
    925 static MHD_RESULT
    926 con_val_iter (void *cls,
    927               enum MHD_ValueKind kind,
    928               const char *key,
    929               const char *value)
    930 {
    931   struct HttpRequest *hr = cls;
    932   char *hdr;
    933   char *new_value = NULL;
    934 
    935   (void) kind;
    936   if (0 == strcmp (MHD_HTTP_HEADER_HOST,
    937                    key))
    938   {
    939     /* We don't take the host header as given in the request.
    940      * We'll instead put the proxied service's hostname in it*/
    941     return MHD_YES;
    942   }
    943 
    944   if ((0 == strcmp (MHD_HTTP_HEADER_ACCEPT_ENCODING,
    945                     key)) ||
    946       (0 == strcmp (MHD_HTTP_HEADER_CONTENT_ENCODING,
    947                     key)))
    948   {
    949     TWISTER_LOG_INFO ("Do not re-compress request and/or do not"
    950                       " ask for compressed responses\n");
    951     return MHD_YES;
    952   }
    953 
    954   if ((0 == strcmp (MHD_HTTP_HEADER_CONTENT_LENGTH,
    955                     key)))
    956   {
    957     TWISTER_LOG_INFO (
    958       "Do not re-set Content-Length for request (CURLOPT_POSTFIELDSIZE did)\n");
    959     return MHD_YES;
    960   }
    961 
    962   GNUNET_asprintf (&hdr,
    963                    "%s: %s",
    964                    key,
    965                    value);
    966 
    967   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    968               "Adding header `%s' to HTTP request\n",
    969               hdr);
    970   hr->headers = curl_slist_append (hr->headers,
    971                                    hdr);
    972   GNUNET_free (hdr);
    973   GNUNET_free (new_value);
    974   return MHD_YES;
    975 }
    976 
    977 
    978 /**
    979  * Walk a JSON object preparing its modification.  Name to
    980  * be changed in 'walk_object'.
    981  *
    982  * @param path the path pointing to a object to modify.
    983  * @param[out] parent will point to the parent of the targeted
    984  *             object.  This parent will be the "handle" to pass
    985  *             to the jansson modification function.
    986  * @param[out] target last token of the path.  E.g. given a x.y.z,
    987  *             will point to 'z'.  This value will also be passed
    988  *             to the jansson modification function.
    989  *             To be freed by the caller.
    990  * @param json the object to be walked over.
    991  * @return #GNUNET_OK if @a path was valid.
    992  */
    993 static unsigned int
    994 walk_object (const char *path,
    995              json_t **parent,
    996              char **target,
    997              json_t *json)
    998 {
    999   json_t *element;
   1000   json_t *cur;
   1001   char *token;
   1002   char *last_token;
   1003   char *path_dup;
   1004 
   1005   GNUNET_assert (NULL != json);
   1006   GNUNET_asprintf (&path_dup,
   1007                    ".%s", /* Make sure path starts with dot. */
   1008                    path);
   1009 
   1010   last_token = strrchr (path_dup, '.') + 1;
   1011   /* Give first nondelim char. */
   1012   token = strtok (path_dup, ".");
   1013   if (NULL == (element = json))
   1014   {
   1015     TWISTER_LOG_ERROR ("Attempting to walk a non JSON response!\n");
   1016     return GNUNET_SYSERR;
   1017   }
   1018 
   1019   while (last_token != token)
   1020   {
   1021     TWISTER_LOG_DEBUG ("token/last_token: %s@%p / %s@%p\n",
   1022                        token, token,
   1023                        last_token, last_token);
   1024     if (NULL == token)
   1025       return GNUNET_SYSERR; // path was ".", refuse to process it.
   1026     if (NULL != (cur = json_object_get (element,
   1027                                         token)))
   1028     {
   1029       element = cur;
   1030       token = strtok (NULL, ".");
   1031       continue;
   1032     }
   1033     if (NULL != (cur = json_array_get (element,
   1034                                        (unsigned int) strtoul
   1035                                          (token, NULL, 10))))
   1036     {
   1037       element = cur;
   1038       token = strtok (NULL, ".");
   1039       continue;
   1040     }
   1041     TWISTER_LOG_WARNING ("Path token '%s' not found\n",
   1042                          token);
   1043     GNUNET_free (path_dup);
   1044     return GNUNET_NO;
   1045   }
   1046 
   1047   if ( (NULL == json_object_get (element,
   1048                                  last_token) ) &&
   1049        /* NOTE: if token is bad but converts to either 0, or
   1050         * ULONG max AND the array has such a index, then this
   1051         * test won't detect any error. Likewise, the method for
   1052         * deleting/modifying the response will operate on that
   1053         * same random array element. */(NULL == json_array_get (element, (unsigned int) strtoul
   1054                                   (token, NULL, 10))) )
   1055   {
   1056     TWISTER_LOG_WARNING ("(Last) path token '%s' not found\n",
   1057                          last_token);
   1058     GNUNET_free (path_dup);
   1059     return GNUNET_NO;
   1060   }
   1061 
   1062   *target = GNUNET_strdup (last_token);
   1063   *parent = element;
   1064   GNUNET_free (path_dup);
   1065   return GNUNET_OK;
   1066 }
   1067 
   1068 
   1069 /**
   1070  * Modify a JSON object.  NOTE, the new value to set is
   1071  * taken from the global value `modify_value'.
   1072  *
   1073  * @param con HTTP connection handle.
   1074  *        FIXME: deprecated, to be removed.
   1075  * @param json the JSON object to modify.
   1076  * @param path the path to the field to modify.
   1077  */
   1078 static int
   1079 modify_object (struct MHD_Connection *con,
   1080                json_t *json,
   1081                char *path)
   1082 {
   1083 
   1084   char *target;
   1085   int ret_modify;
   1086   json_t *parent;
   1087   json_t *new_value;
   1088   json_error_t error;
   1089 
   1090   if (GNUNET_OK != walk_object (path,
   1091                                 &parent,
   1092                                 &target,
   1093                                 json))
   1094   {
   1095     TWISTER_LOG_INFO ("Path (%s) was not found on this object\n",
   1096                       path);
   1097     return GNUNET_NO;
   1098   }
   1099 
   1100   /* At this point, the parent and the target are pointed to. */
   1101 
   1102   if (0 == strcmp ("true", modify_value))
   1103   {
   1104     TWISTER_LOG_DEBUG ("New value parsed as boolean true\n");
   1105     new_value = json_true ();
   1106     goto perform_modbody;
   1107   }
   1108 
   1109   if (NULL != (new_value = json_loads (modify_value,
   1110                                        JSON_REJECT_DUPLICATES
   1111                                        | JSON_DISABLE_EOF_CHECK,
   1112                                        &error)))
   1113   {
   1114     TWISTER_LOG_DEBUG ("New value parsed as object/array\n");
   1115     goto perform_modbody;
   1116   }
   1117 
   1118   if (NULL != (new_value = json_string (modify_value)))
   1119   {
   1120     TWISTER_LOG_DEBUG ("New value parsed as string\n");
   1121     goto perform_modbody;
   1122   }
   1123 
   1124   TWISTER_LOG_ERROR ("Invalid new value given: %s\n",
   1125                      modify_value);
   1126   GNUNET_free (target);
   1127   return GNUNET_SYSERR;
   1128 
   1129 perform_modbody:
   1130   ret_modify = -1;
   1131   if (json_is_object (parent))
   1132     ret_modify = json_object_set (parent,
   1133                                   target,
   1134                                   new_value);
   1135   if (json_is_array (parent))
   1136     ret_modify = json_array_set_new
   1137                    (parent,
   1138                    (unsigned int) strtoul (target, NULL, 10),
   1139                    new_value);
   1140 
   1141   GNUNET_free (target);
   1142   json_decref (new_value);
   1143 
   1144   if (-1 == ret_modify)
   1145   {
   1146     TWISTER_LOG_WARNING ("Could not replace '%s'\n", target);
   1147     return GNUNET_SYSERR;
   1148   }
   1149 
   1150   return GNUNET_OK;
   1151 }
   1152 
   1153 
   1154 /**
   1155  * Flip a random character to the string pointed to by @a path.
   1156  *
   1157  * @param con FIXME deprecated.
   1158  * @param json the object whose field will be flipped.
   1159  * @param flip_path the path to the string-field to flip.
   1160  * @return #GNUNET_OK when the path was found, and flipped.
   1161  */
   1162 static enum GNUNET_GenericReturnValue
   1163 flip_object (struct MHD_Connection *con,
   1164              json_t *json,
   1165              const char *flip_path)
   1166 {
   1167   char *target;
   1168   json_t *parent;
   1169   json_t *child = NULL;
   1170   const char *current_value;
   1171   char *current_value_flip;
   1172   unsigned int crockford_index;
   1173   unsigned int flip_index;
   1174   size_t len;
   1175   const char crockford_chars[] = {
   1176     '0', '1', '2', '3', '4', '5',
   1177     '6', '7', '8', '9', 'A', 'B',
   1178     'C', 'D', 'E', 'F', 'G', 'H',
   1179     'J', 'K', 'M', 'N', 'P', 'Q',
   1180     'R', 'S', 'T', 'V', 'W', 'X',
   1181     'Y', 'Z'
   1182   }; /* index: 0-31 */
   1183 #define CROCKFORD_MAX_INDEX (sizeof (crockford_chars))
   1184 
   1185   if (NULL == json)
   1186     return GNUNET_NO;
   1187   if (GNUNET_OK != walk_object (flip_path,
   1188                                 &parent,
   1189                                 &target,
   1190                                 json))
   1191   {
   1192     /**
   1193      * Not an error, as the user can "batch"
   1194      * requests until the right object gets in the way.
   1195      */
   1196     TWISTER_LOG_INFO ("Path (%s) was not found on this object\n",
   1197                       flip_path);
   1198     return GNUNET_NO;
   1199   }
   1200 
   1201 
   1202   if (json_is_object (parent))
   1203     child = json_object_get (parent,
   1204                              target);
   1205   if (json_is_array (parent))
   1206     child = json_array_get (parent,
   1207                             (unsigned int) strtoul (target,
   1208                                                     NULL,
   1209                                                     10));
   1210   /* json walker is such that at this point the
   1211    * child's parent is always a object or array.  */
   1212   GNUNET_assert (NULL != child);
   1213   GNUNET_free (target);
   1214 
   1215   current_value = json_string_value (child);
   1216   if (NULL == current_value)
   1217   {
   1218     GNUNET_break_op (0);
   1219     return GNUNET_SYSERR;
   1220   }
   1221   current_value_flip = GNUNET_strdup (current_value);
   1222   len = strlen (current_value_flip);
   1223   if (len > 1)
   1224     len--; /* do NOT flip last character, as that
   1225               one may not matter in base32 encoding */
   1226   do {
   1227     flip_index = GNUNET_CRYPTO_random_u32 (
   1228       len);
   1229     crockford_index = GNUNET_CRYPTO_random_u32 (
   1230       CROCKFORD_MAX_INDEX);
   1231     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1232                 "Try flipping %u to Crockford index: %u\n",
   1233                 flip_index,
   1234                 crockford_index);
   1235   }
   1236   while (current_value_flip[flip_index] ==
   1237          crockford_chars[crockford_index]);
   1238   current_value_flip[flip_index] =
   1239     crockford_chars[crockford_index];
   1240 
   1241   TWISTER_LOG_INFO ("Flipping %s to %s\n",
   1242                     current_value,
   1243                     current_value_flip);
   1244   if (0 != json_string_set (child,
   1245                             current_value_flip))
   1246   {
   1247     TWISTER_LOG_WARNING ("Could not flip '%s'\n",
   1248                          flip_path);
   1249     GNUNET_free (current_value_flip);
   1250     return GNUNET_SYSERR;
   1251   }
   1252   GNUNET_free (current_value_flip);
   1253   return GNUNET_OK;
   1254 }
   1255 
   1256 
   1257 /**
   1258  * Delete object within the proxied response.
   1259  * Always queues a response; only deletes the object if it is
   1260  * found within the response, otherwise return it verbatim (but
   1261  * will look for it into the next response).  Will flush the
   1262  * operation once the wanted object has been found.
   1263  *
   1264  * @param con FIXME deprecated.
   1265  * @param hr contains the object whose field will be deleted.
   1266  * @return #GNUNET_OK when the path was found, and deleted.
   1267  */
   1268 static enum GNUNET_GenericReturnValue
   1269 delete_object (struct MHD_Connection *con,
   1270                struct HttpRequest *hr)
   1271 {
   1272   char *target;
   1273   json_t *parent;
   1274   int ret_deletion = -1;
   1275 
   1276   if (NULL == hr->json)
   1277     return GNUNET_NO;
   1278   if (GNUNET_OK != walk_object (delete_path,
   1279                                 &parent,
   1280                                 &target,
   1281                                 hr->json))
   1282   {
   1283     TWISTER_LOG_INFO ("Path (%s) was not found on this object\n",
   1284                       delete_path);
   1285     return GNUNET_NO;
   1286   }
   1287 
   1288   /* here, element is the parent of the element to be deleted. */
   1289   if (json_is_object (parent))
   1290     ret_deletion = json_object_del (parent, target);
   1291 
   1292   if (json_is_array (parent))
   1293   {
   1294     ret_deletion = json_array_remove
   1295                      (parent, (unsigned int) strtoul (target,
   1296                                                       NULL,
   1297                                                       10));
   1298   }
   1299   GNUNET_free (target);
   1300 
   1301   if (-1 == ret_deletion)
   1302   {
   1303     TWISTER_LOG_WARNING ("Could not delete '%s'\n",
   1304                          target);
   1305     return GNUNET_SYSERR;
   1306   }
   1307   return GNUNET_OK;
   1308 }
   1309 
   1310 
   1311 /**
   1312  * Decompress data.
   1313  *
   1314  * @param request contains input data to inflate
   1315  * @return result code indicating the status of the operation
   1316  */
   1317 static int
   1318 inflate_data (struct HttpRequest *request)
   1319 {
   1320   z_stream z;
   1321   char *tmp;
   1322   size_t tmp_size;
   1323   int ret;
   1324 
   1325   memset (&z, 0, sizeof (z));
   1326   z.next_in = (Bytef *) request->io_buf;
   1327   z.avail_in = request->io_len;
   1328   tmp_size = GNUNET_MIN (REQUEST_BUFFER_MAX,
   1329                          request->io_len * 4);
   1330   tmp = GNUNET_malloc (tmp_size);
   1331   z.next_out = (Bytef *) tmp;
   1332   z.avail_out = tmp_size;
   1333   ret = inflateInit (&z);
   1334   switch (ret)
   1335   {
   1336   case Z_MEM_ERROR:
   1337     GNUNET_break (0);
   1338     return GNUNET_MHD_PR_OUT_OF_MEMORY;
   1339   case Z_STREAM_ERROR:
   1340     GNUNET_break_op (0);
   1341     return GNUNET_MHD_PR_JSON_INVALID;
   1342   case Z_OK:
   1343     break;
   1344   }
   1345   while (1)
   1346   {
   1347     ret = inflate (&z, 0);
   1348     switch (ret)
   1349     {
   1350     case Z_MEM_ERROR:
   1351       GNUNET_break (0);
   1352       GNUNET_break (Z_OK == inflateEnd (&z));
   1353       GNUNET_free (tmp);
   1354       return GNUNET_MHD_PR_OUT_OF_MEMORY;
   1355     case Z_DATA_ERROR:
   1356       GNUNET_break (0);
   1357       GNUNET_break (Z_OK == inflateEnd (&z));
   1358       GNUNET_free (tmp);
   1359       return GNUNET_MHD_PR_JSON_INVALID;
   1360     case Z_NEED_DICT:
   1361       GNUNET_break (0);
   1362       GNUNET_break (Z_OK == inflateEnd (&z));
   1363       GNUNET_free (tmp);
   1364       return GNUNET_MHD_PR_JSON_INVALID;
   1365     case Z_OK:
   1366       if ((0 < z.avail_out) && (0 == z.avail_in))
   1367       {
   1368         /* truncated input stream */
   1369         GNUNET_break (0);
   1370         GNUNET_break (Z_OK == inflateEnd (&z));
   1371         GNUNET_free (tmp);
   1372         return GNUNET_MHD_PR_JSON_INVALID;
   1373       }
   1374       if (0 < z.avail_out)
   1375         continue; /* just call it again */
   1376       /* output buffer full, can we grow it? */
   1377       if (tmp_size == REQUEST_BUFFER_MAX)
   1378       {
   1379         /* already at max */
   1380         GNUNET_break (0);
   1381         GNUNET_break (Z_OK == inflateEnd (&z));
   1382         GNUNET_free (tmp);
   1383         return GNUNET_MHD_PR_OUT_OF_MEMORY;
   1384       }
   1385       if (tmp_size * 2 < tmp_size)
   1386         tmp_size = REQUEST_BUFFER_MAX;
   1387       else
   1388         tmp_size = GNUNET_MIN (REQUEST_BUFFER_MAX, tmp_size * 2);
   1389       tmp = GNUNET_realloc (tmp, tmp_size);
   1390       z.next_out = (Bytef *) &tmp[z.total_out];
   1391       continue;
   1392     case Z_STREAM_END:
   1393       /* decompression successful, make 'tmp' the new 'data' */
   1394       GNUNET_free (request->io_buf);
   1395       request->io_buf = tmp;
   1396       request->io_size = tmp_size;
   1397       request->io_len = z.total_out;
   1398       GNUNET_break (Z_OK == inflateEnd (&z));
   1399       return GNUNET_MHD_PR_SUCCESS; /* at least for now */
   1400     }
   1401   } /* while (1) */
   1402 }
   1403 
   1404 
   1405 /**
   1406  * Create the response object according to the "chaos rate".
   1407  * If this latter strikes, then the response will be "503 Service
   1408  * Unavailable" with a empty body (overriding every other mod that
   1409  * the user might have given.)
   1410  *
   1411  * @param hr the HTTP object representing the current state.
   1412  */
   1413 static void
   1414 create_response_with_chaos_rate (struct HttpRequest *hr)
   1415 {
   1416   uint64_t random;
   1417   const void *resp_buf;
   1418   size_t resp_len;
   1419 
   1420   random = GNUNET_CRYPTO_random_u64 (100);
   1421   TWISTER_LOG_INFO ("p: %llu, random: %llu\n",
   1422                     (unsigned long long) chaos_rate,
   1423                     (unsigned long long) random);
   1424   if (random < chaos_rate)
   1425   {
   1426     /* p won */
   1427     TWISTER_LOG_INFO ("Chaos probability won the case.\n");
   1428     resp_buf = "Service unavailable";
   1429     resp_len = strlen (resp_buf);
   1430     hr->response_code = MHD_HTTP_SERVICE_UNAVAILABLE;
   1431   }
   1432   else
   1433   {
   1434     resp_len = hr->io_len;
   1435     resp_buf = hr->io_buf;
   1436   }
   1437   hr->response
   1438     = MHD_create_response_from_buffer_copy (resp_len,
   1439                                             resp_buf);
   1440 }
   1441 
   1442 
   1443 /**
   1444  * Main MHD callback for handling requests.
   1445  *
   1446  * @param cls unused
   1447  * @param con MHD connection handle
   1448  * @param url the url in the request
   1449  * @param meth the HTTP method used ("GET", "PUT", etc.)
   1450  * @param ver the HTTP version string (i.e. "HTTP/1.1")
   1451  * @param upload_data the data being uploaded (excluding HEADERS,
   1452  *        for a POST that fits into memory and that is encoded
   1453  *        with a supported encoding, the POST data will NOT be
   1454  *        given in upload_data and is instead available as
   1455  *        part of MHD_get_connection_values; very large POST
   1456  *        data *will* be made available incrementally in
   1457  *        upload_data)
   1458  * @param upload_data_size set initially to the size of the
   1459  *        @a upload_data provided; the method must update this
   1460  *        value to the number of bytes NOT processed;
   1461  * @param con_cls pointer to location where we store the
   1462  *        'struct Request'
   1463  * @return #MHD_YES if the connection was handled successfully,
   1464  *         #MHD_NO if the socket must be closed due to a serious
   1465  *         error while handling the request
   1466  */
   1467 static MHD_RESULT
   1468 create_response (void *cls,
   1469                  struct MHD_Connection *con,
   1470                  const char *url,
   1471                  const char *meth,
   1472                  const char *ver,
   1473                  const char *upload_data,
   1474                  size_t *upload_data_size,
   1475                  void **con_cls)
   1476 {
   1477   struct HttpRequest *hr = *con_cls;
   1478 
   1479   (void) cls;
   1480   (void) url;
   1481 
   1482   if (NULL == hr)
   1483   {
   1484     GNUNET_break (0);
   1485     return MHD_NO;
   1486   }
   1487 
   1488   if (REQUEST_STATE_WITH_MHD == hr->state)
   1489   {
   1490     hr->state = REQUEST_STATE_CLIENT_UPLOAD_STARTED;
   1491     /* TODO: hacks for 100 continue suppression would go here! */
   1492     return MHD_YES;
   1493   }
   1494 
   1495   /* continuing to process request */
   1496   if (0 != *upload_data_size)
   1497   {
   1498     GNUNET_assert
   1499       (REQUEST_STATE_CLIENT_UPLOAD_STARTED == hr->state);
   1500 
   1501     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1502                 "Processing %u bytes UPLOAD\n",
   1503                 (unsigned int) *upload_data_size);
   1504 
   1505     /* Grow the buffer if remaining space isn't enough.  */
   1506     if (hr->io_size - hr->io_len < *upload_data_size)
   1507     {
   1508       /* How can this assertion be false?  */
   1509       GNUNET_assert (hr->io_size * 2 + 1024 > hr->io_size);
   1510       /* This asserts that upload_data_size > 0, ?  */
   1511       GNUNET_assert (*upload_data_size + hr->io_len > hr->io_len);
   1512 
   1513       GNUNET_array_grow (hr->io_buf,
   1514                          hr->io_size,
   1515                          GNUNET_MAX
   1516                            (hr->io_size * 2 + 1024,
   1517                            *upload_data_size + hr->io_len));
   1518     }
   1519 
   1520     /* Finally copy upload data.  */
   1521     GNUNET_memcpy (&hr->io_buf[hr->io_len],
   1522                    upload_data,
   1523                    *upload_data_size);
   1524 
   1525     hr->io_len += *upload_data_size;
   1526     *upload_data_size = 0;
   1527 
   1528     return MHD_YES;
   1529   }
   1530 
   1531   /* Upload (*from the client*) finished or just a without-body
   1532    * request.  */
   1533   if (REQUEST_STATE_CLIENT_UPLOAD_STARTED == hr->state)
   1534   {
   1535     hr->state = REQUEST_STATE_CLIENT_UPLOAD_DONE;
   1536     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1537                 "Finished processing UPLOAD\n");
   1538     if (0 != hr->io_len)
   1539     {
   1540       const char *ce;
   1541 
   1542       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1543                   "Attempting to decompress\n");
   1544       ce = MHD_lookup_connection_value
   1545              (con,
   1546              MHD_HEADER_KIND,
   1547              MHD_HTTP_HEADER_CONTENT_ENCODING);
   1548 
   1549       if ((NULL != ce) && (0 == strcmp ("deflate", ce)))
   1550         GNUNET_assert (Z_OK == inflate_data (hr));
   1551 
   1552       ce = MHD_lookup_connection_value
   1553              (con,
   1554              MHD_HEADER_KIND,
   1555              MHD_HTTP_HEADER_CONTENT_TYPE);
   1556 
   1557       if ((NULL != ce) && (0 == strcmp ("application/json", ce)))
   1558       {
   1559         json_error_t error;
   1560 
   1561         hr->json = json_loadb (hr->io_buf,
   1562                                hr->io_len,
   1563                                JSON_DECODE_ANY,
   1564                                &error);
   1565         if (NULL == hr->json)
   1566         {
   1567           TWISTER_LOG_ERROR
   1568             ("Could not parse JSON from client: %s (%s)\n",
   1569             error.text,
   1570             error.source);
   1571           /* Quick and dirty.  */
   1572           return MHD_NO;
   1573         }
   1574       }
   1575     }
   1576   }
   1577 
   1578   /* generate curl request to the proxied service. */
   1579   if (NULL == hr->curl)
   1580   {
   1581     /* Malform request body.  Note, this flag will be
   1582      * cleared only after having set the right malformed
   1583      * body length in the request headers.  */
   1584     if ( (GNUNET_YES == malform_upload) &&
   1585          (hr->io_len > 0) )
   1586     {
   1587       TWISTER_LOG_DEBUG ("Will (badly) truncate the request.\n");
   1588       malformed_size = GNUNET_CRYPTO_random_u32 (
   1589                                                  hr->io_len);
   1590       hr->io_len = malformed_size;
   1591       malform_upload = GNUNET_NO;
   1592     }
   1593 
   1594     if (NULL != flip_path_ul)
   1595     {
   1596       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1597                   "Will flip path in request: %s\n",
   1598                   flip_path_ul);
   1599 
   1600       if (GNUNET_OK ==
   1601           flip_object (con,
   1602                        hr->json,
   1603                        flip_path_ul))
   1604       {
   1605         GNUNET_free (flip_path_ul);
   1606         flip_path_ul = NULL;
   1607       }
   1608     }
   1609 
   1610     if (NULL != modify_path_ul)
   1611     {
   1612       int ret;
   1613 
   1614       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1615                   "Will try to modify: %s\n",
   1616                   flip_path_ul);
   1617 
   1618       ret = modify_object (con,
   1619                            hr->json,
   1620                            modify_path_ul);
   1621 
   1622       if ((GNUNET_OK == ret) || (GNUNET_SYSERR == ret))
   1623       {
   1624         GNUNET_free (modify_path_ul);
   1625         GNUNET_free (modify_value);
   1626 
   1627         modify_path_ul = NULL;
   1628         modify_value = NULL;
   1629       }
   1630     }
   1631 
   1632     /* Existing io_len is enough to accommodate this encoding. */
   1633     json_dumpb (hr->json,
   1634                 hr->io_buf,
   1635                 hr->io_len,
   1636                 JSON_COMPACT);
   1637     json_decref (hr->json);
   1638     hr->json = NULL;
   1639 
   1640     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1641                 "Generating curl request\n");
   1642     hr->curl = curl_easy_init ();
   1643     if (NULL == hr->curl)
   1644     {
   1645       TWISTER_LOG_ERROR ("Could not init the curl handle\n");
   1646       return MHD_queue_response (con,
   1647                                  MHD_HTTP_INTERNAL_SERVER_ERROR,
   1648                                  curl_failure_response);
   1649     }
   1650 
   1651     /* No need to check whether we're POSTing or PUTting.
   1652      * If not needed, one of the following values will be
   1653      * ignored.*/
   1654     curl_easy_setopt (hr->curl,
   1655                       CURLOPT_POSTFIELDSIZE,
   1656                       hr->io_len);
   1657     curl_easy_setopt (hr->curl,
   1658                       CURLOPT_INFILESIZE,
   1659                       hr->io_len);
   1660     curl_easy_setopt (hr->curl,
   1661                       CURLOPT_HEADERFUNCTION,
   1662                       &curl_check_hdr);
   1663     curl_easy_setopt (hr->curl,
   1664                       CURLOPT_HEADERDATA,
   1665                       hr);
   1666     curl_easy_setopt (hr->curl,
   1667                       CURLOPT_FOLLOWLOCATION,
   1668                       0);
   1669     curl_easy_setopt (hr->curl,
   1670                       CURLOPT_CONNECTTIMEOUT,
   1671                       600L);
   1672     curl_easy_setopt (hr->curl,
   1673                       CURLOPT_TIMEOUT,
   1674                       600L);
   1675     curl_easy_setopt (hr->curl,
   1676                       CURLOPT_NOSIGNAL,
   1677                       1L);
   1678     curl_easy_setopt (hr->curl,
   1679                       CURLOPT_PRIVATE,
   1680                       hr);
   1681     curl_easy_setopt (hr->curl,
   1682                       CURLOPT_VERBOSE,
   1683                       0);
   1684 
   1685     curl_easy_setopt (hr->curl,
   1686                       CURLOPT_READFUNCTION,
   1687                       &curl_upload_cb);
   1688 
   1689     curl_easy_setopt (hr->curl,
   1690                       CURLOPT_READDATA,
   1691                       hr);
   1692 
   1693     curl_easy_setopt (hr->curl,
   1694                       CURLOPT_WRITEFUNCTION,
   1695                       &curl_download_cb);
   1696 
   1697     curl_easy_setopt (hr->curl,
   1698                       CURLOPT_WRITEDATA,
   1699                       hr);
   1700     {
   1701       char *curlurl;
   1702       char *host_hdr;
   1703 
   1704       GNUNET_asprintf (&curlurl,
   1705                        "%s%s",
   1706                        target_server_base_url,
   1707                        hr->url);
   1708       curl_easy_setopt (hr->curl,
   1709                         CURLOPT_URL,
   1710                         curlurl);
   1711       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1712                   "Forwarding request to: %s\n",
   1713                   curlurl);
   1714       GNUNET_free (curlurl);
   1715 
   1716       host_hdr = build_host_header (target_server_base_url);
   1717       TWISTER_LOG_DEBUG ("Faking the host header, %s\n",
   1718                          host_hdr);
   1719       hr->headers = curl_slist_append (hr->headers,
   1720                                        host_hdr);
   1721       GNUNET_free (host_hdr);
   1722     }
   1723 
   1724     if (0 == strcasecmp (meth,
   1725                          MHD_HTTP_METHOD_PUT))
   1726     {
   1727       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1728                   "Crafting a CURL PUT request\n");
   1729 
   1730       curl_easy_setopt (hr->curl,
   1731                         CURLOPT_UPLOAD,
   1732                         1L);
   1733       hr->state = REQUEST_STATE_PROXY_UPLOAD_STARTED;
   1734 
   1735     }
   1736     else if (0 == strcasecmp (meth,
   1737                               MHD_HTTP_METHOD_POST))
   1738     {
   1739       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1740                   "Crafting a CURL POST request\n");
   1741       curl_easy_setopt (hr->curl,
   1742                         CURLOPT_POST,
   1743                         1L);
   1744       curl_easy_setopt (hr->curl,
   1745                         CURLOPT_VERBOSE,
   1746                         1L);
   1747       hr->state = REQUEST_STATE_PROXY_UPLOAD_STARTED;
   1748 
   1749     }
   1750     else if (0 == strcasecmp (meth,
   1751                               MHD_HTTP_METHOD_HEAD))
   1752     {
   1753       hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
   1754       curl_easy_setopt (hr->curl,
   1755                         CURLOPT_NOBODY,
   1756                         1L);
   1757     }
   1758     else if (0 == strcasecmp (meth,
   1759                               MHD_HTTP_METHOD_OPTIONS))
   1760     {
   1761       hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
   1762       curl_easy_setopt (hr->curl,
   1763                         CURLOPT_CUSTOMREQUEST,
   1764                         "OPTIONS");
   1765     }
   1766     else if (0 == strcasecmp (meth,
   1767                               MHD_HTTP_METHOD_GET))
   1768     {
   1769       hr->state = REQUEST_STATE_PROXY_DOWNLOAD_STARTED;
   1770       curl_easy_setopt (hr->curl,
   1771                         CURLOPT_HTTPGET,
   1772                         1L);
   1773     }
   1774     else
   1775     {
   1776       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1777                   "Unsupported HTTP method `%s'\n",
   1778                   meth);
   1779       curl_easy_cleanup (hr->curl);
   1780       hr->curl = NULL;
   1781       return MHD_NO;
   1782     }
   1783 
   1784     if (0 == strcasecmp (ver,
   1785                          MHD_HTTP_VERSION_1_0))
   1786     {
   1787       curl_easy_setopt (hr->curl,
   1788                         CURLOPT_HTTP_VERSION,
   1789                         CURL_HTTP_VERSION_1_0);
   1790     }
   1791     else if (0 == strcasecmp (ver,
   1792                               MHD_HTTP_VERSION_1_1))
   1793     {
   1794       curl_easy_setopt (hr->curl,
   1795                         CURLOPT_HTTP_VERSION,
   1796                         CURL_HTTP_VERSION_1_1);
   1797     }
   1798     else
   1799     {
   1800       curl_easy_setopt (hr->curl,
   1801                         CURLOPT_HTTP_VERSION,
   1802                         CURL_HTTP_VERSION_NONE);
   1803     }
   1804 
   1805     if (CURLM_OK != curl_multi_add_handle (curl_multi,
   1806                                            hr->curl))
   1807     {
   1808       GNUNET_break (0);
   1809       curl_easy_cleanup (hr->curl);
   1810       hr->curl = NULL;
   1811       return MHD_NO;
   1812     }
   1813 
   1814     MHD_get_connection_values (con,
   1815                                MHD_HEADER_KIND,
   1816                                &con_val_iter,
   1817                                hr);
   1818 
   1819     curl_easy_setopt (hr->curl,
   1820                       CURLOPT_HTTPHEADER,
   1821                       hr->headers);
   1822     curl_download_prepare ();
   1823 
   1824     return MHD_YES;
   1825   }
   1826 
   1827   if (REQUEST_STATE_PROXY_DOWNLOAD_DONE != hr->state)
   1828   {
   1829     GNUNET_assert (GNUNET_NO == hr->suspended);
   1830     MHD_suspend_connection (con);
   1831     hr->suspended = GNUNET_YES;
   1832     return MHD_YES; /* wait for curl */
   1833   }
   1834 
   1835   GNUNET_assert (REQUEST_STATE_PROXY_DOWNLOAD_DONE == hr->state);
   1836   if (0 != hack_response_code)
   1837   {
   1838     hr->response_code = hack_response_code;
   1839     hack_response_code = 0; /* reset for next request */
   1840   }
   1841 
   1842   if (NULL != flip_path_dl)
   1843   {
   1844     TWISTER_LOG_DEBUG ("Will flip path"
   1845                        " in response: %s\n",
   1846                        flip_path_dl);
   1847 
   1848     if (GNUNET_OK == flip_object (con,
   1849                                   hr->json,
   1850                                   flip_path_dl))
   1851     {
   1852       GNUNET_free (flip_path_dl);
   1853       flip_path_dl = NULL;
   1854     }
   1855   }
   1856 
   1857   if (NULL != delete_path)
   1858   {
   1859     TWISTER_LOG_DEBUG ("Will delete path: %s\n",
   1860                        delete_path);
   1861     if (GNUNET_OK == delete_object (con,
   1862                                     hr))
   1863     {
   1864       GNUNET_free (delete_path);
   1865       delete_path = NULL;
   1866     }
   1867   }
   1868 
   1869   if (NULL != modify_path_dl)
   1870   {
   1871     enum GNUNET_GenericReturnValue ret;
   1872 
   1873     TWISTER_LOG_DEBUG ("Will modify path: %s to value %s\n",
   1874                        modify_path_dl,
   1875                        modify_value);
   1876     ret = modify_object (con,
   1877                          hr->json,
   1878                          modify_path_dl);
   1879     if ((GNUNET_OK == ret) || (GNUNET_SYSERR == ret))
   1880     {
   1881       GNUNET_free (modify_path_dl);
   1882       GNUNET_free (modify_value);
   1883 
   1884       modify_path_dl = NULL;
   1885       modify_value = NULL;
   1886     }
   1887   }
   1888 
   1889   if (NULL != hr->json)
   1890   {
   1891     GNUNET_free (hr->io_buf);
   1892     hr->io_buf = json_dumps (hr->json,
   1893                              JSON_COMPACT);
   1894     if (NULL != hr->io_buf)
   1895     {
   1896       hr->io_len = strlen (hr->io_buf);
   1897     }
   1898     else
   1899     {
   1900       GNUNET_break (0);
   1901       hr->io_len = 0;
   1902     }
   1903     json_decref (hr->json);
   1904     hr->json = NULL;
   1905   }
   1906 
   1907   if (GNUNET_YES == malform)
   1908   {
   1909     TWISTER_LOG_DEBUG ("Will (badly) truncate the response.\n");
   1910     if (hr->io_len > 0)
   1911     {
   1912       size_t fake_len;
   1913 
   1914       fake_len = GNUNET_CRYPTO_random_u32 (
   1915                                            hr->io_len);
   1916       hr->io_len = fake_len;
   1917     }
   1918     malform = GNUNET_NO;
   1919   }
   1920 
   1921   create_response_with_chaos_rate (hr);
   1922 
   1923   for (struct HttpResponseHeader *header = hr->header_head;
   1924        NULL != header;
   1925        header = header->next)
   1926   {
   1927     const char *value = header->value;
   1928 
   1929     if ((NULL != modify_header_dl) &&
   1930         (0 == strcmp (header->type,
   1931                       modify_header_dl)))
   1932     {
   1933       value = modify_value;
   1934     }
   1935     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1936                 "Adding MHD response header %s->%s\n",
   1937                 header->type,
   1938                 value);
   1939     GNUNET_break (MHD_YES ==
   1940                   MHD_add_response_header (hr->response,
   1941                                            header->type,
   1942                                            value));
   1943   }
   1944   run_mhd_now ();
   1945 
   1946   return MHD_queue_response (con,
   1947                              hr->response_code,
   1948                              hr->response);
   1949 }
   1950 
   1951 
   1952 /* ************ MHD HTTP setup and event loop *************** */
   1953 
   1954 
   1955 /**
   1956  * Function called when MHD decides that we
   1957  * are done with a request.
   1958  *
   1959  * @param cls NULL
   1960  * @param connection connection handle
   1961  * @param con_cls value as set by the last call to
   1962  *        the MHD_AccessHandlerCallback, should be
   1963  *        our `struct HttpRequest *` (set by `create_response()`)
   1964  * @param toe reason for request termination (ignored)
   1965  */
   1966 static void
   1967 mhd_completed_cb (void *cls,
   1968                   struct MHD_Connection *connection,
   1969                   void **con_cls,
   1970                   enum MHD_RequestTerminationCode toe)
   1971 {
   1972   struct HttpRequest *hr = *con_cls;
   1973   struct HttpResponseHeader *header;
   1974 
   1975   (void) cls;
   1976   (void) connection;
   1977   if (NULL == hr)
   1978     return;
   1979   if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
   1980     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1981                 "MHD encountered error handling request: %d\n",
   1982                 toe);
   1983   if (NULL != hr->curl)
   1984   {
   1985     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1986                 "Resetting cURL handle\n");
   1987     curl_multi_remove_handle (curl_multi,
   1988                               hr->curl);
   1989     curl_easy_cleanup (hr->curl);
   1990     hr->curl = NULL;
   1991     hr->io_len = 0;
   1992   }
   1993   if (NULL != hr->headers)
   1994   {
   1995     curl_slist_free_all (hr->headers);
   1996     hr->headers = NULL;
   1997   }
   1998   if ( (NULL != hr->response) &&
   1999        (curl_failure_response != hr->response) )
   2000     /* Destroy non-error responses... (?) */
   2001     MHD_destroy_response (hr->response);
   2002 
   2003   for (header = hr->header_head;
   2004        header != NULL;
   2005        header = hr->header_head)
   2006   {
   2007     GNUNET_CONTAINER_DLL_remove (hr->header_head,
   2008                                  hr->header_tail,
   2009                                  header);
   2010     GNUNET_free (header->type);
   2011     GNUNET_free (header->value);
   2012     GNUNET_free (header);
   2013   }
   2014 
   2015   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2016               "Proxying of '%s' completely done\n",
   2017               hr->url);
   2018 
   2019   GNUNET_free (hr->url);
   2020   GNUNET_free (hr->io_buf);
   2021   GNUNET_CONTAINER_DLL_remove (hr_head,
   2022                                hr_tail,
   2023                                hr);
   2024   GNUNET_free (hr);
   2025   *con_cls = NULL;
   2026 }
   2027 
   2028 
   2029 /**
   2030  * Function called when MHD first processes an incoming connection.
   2031  * Gives us the respective URI information.
   2032  *
   2033  * We use this to associate the `struct MHD_Connection` with our
   2034  * internal `struct HttpRequest` data structure (by checking
   2035  * for matching sockets).
   2036  *
   2037  * @param cls the HTTP server handle (a `struct MhdHttpList`)
   2038  * @param url the URL that is being requested
   2039  * @param connection MHD connection object for the request
   2040  * @return the `struct HttpRequest` that this @a connection is for
   2041  */
   2042 static void *
   2043 mhd_log_callback (void *cls,
   2044                   const char *url,
   2045                   struct MHD_Connection *connection)
   2046 {
   2047   struct HttpRequest *hr;
   2048   const union MHD_ConnectionInfo *ci;
   2049 
   2050   (void) cls;
   2051   ci = MHD_get_connection_info (connection,
   2052                                 MHD_CONNECTION_INFO_SOCKET_CONTEXT);
   2053   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   2054               "Processing %s\n",
   2055               url);
   2056   if (NULL == ci)
   2057   {
   2058     GNUNET_break (0);
   2059     return NULL;
   2060   }
   2061 
   2062   hr = GNUNET_new (struct HttpRequest);
   2063   hr->con = connection;
   2064   hr->url = GNUNET_strdup (url);
   2065   GNUNET_CONTAINER_DLL_insert (hr_head,
   2066                                hr_tail,
   2067                                hr);
   2068   return hr;
   2069 }
   2070 
   2071 
   2072 /**
   2073  * Kill the MHD daemon.
   2074  */
   2075 static void
   2076 kill_httpd (void)
   2077 {
   2078   MHD_stop_daemon (mhd_daemon);
   2079   mhd_daemon = NULL;
   2080   if (NULL != httpd_task)
   2081   {
   2082     GNUNET_SCHEDULER_cancel (httpd_task);
   2083     httpd_task = NULL;
   2084   }
   2085 }
   2086 
   2087 
   2088 /**
   2089  * Task run whenever HTTP server operations are pending.
   2090  *
   2091  * @param cls the `struct MhdHttpList *`
   2092  *        of the daemon that is being run
   2093  */
   2094 static void
   2095 do_httpd (void *cls);
   2096 
   2097 
   2098 /**
   2099  * Schedule MHD.  This function should be called initially when an
   2100  * MHD is first getting its client socket, and will then
   2101  * automatically always be called later whenever there is work to
   2102  * be done.
   2103  */
   2104 static void
   2105 schedule_httpd (void)
   2106 {
   2107   fd_set rs;
   2108   fd_set ws;
   2109   fd_set es;
   2110   struct GNUNET_NETWORK_FDSet *wrs;
   2111   struct GNUNET_NETWORK_FDSet *wws;
   2112   int max;
   2113   int haveto;
   2114   MHD_UNSIGNED_LONG_LONG timeout;
   2115   struct GNUNET_TIME_Relative tv;
   2116 
   2117   FD_ZERO (&rs);
   2118   FD_ZERO (&ws);
   2119   FD_ZERO (&es);
   2120   max = -1;
   2121   if (MHD_YES !=
   2122       MHD_get_fdset (mhd_daemon,
   2123                      &rs,
   2124                      &ws,
   2125                      &es,
   2126                      &max))
   2127   {
   2128     kill_httpd ();
   2129     return;
   2130   }
   2131   haveto = MHD_get_timeout (mhd_daemon,
   2132                             &timeout);
   2133   if (MHD_YES == haveto)
   2134     tv.rel_value_us = (uint64_t) timeout * 1000LL;
   2135   else
   2136     tv = GNUNET_TIME_UNIT_FOREVER_REL;
   2137   if (-1 != max)
   2138   {
   2139     wrs = GNUNET_NETWORK_fdset_create ();
   2140     wws = GNUNET_NETWORK_fdset_create ();
   2141     GNUNET_NETWORK_fdset_copy_native (wrs,
   2142                                       &rs,
   2143                                       max + 1);
   2144     GNUNET_NETWORK_fdset_copy_native (wws,
   2145                                       &ws,
   2146                                       max + 1);
   2147   }
   2148   else
   2149   {
   2150     wrs = NULL;
   2151     wws = NULL;
   2152   }
   2153   if (NULL != httpd_task)
   2154   {
   2155     GNUNET_SCHEDULER_cancel (httpd_task);
   2156     httpd_task = NULL;
   2157   }
   2158   httpd_task =
   2159     GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
   2160                                  tv,
   2161                                  wrs,
   2162                                  wws,
   2163                                  &do_httpd,
   2164                                  NULL);
   2165   if (NULL != wrs)
   2166     GNUNET_NETWORK_fdset_destroy (wrs);
   2167   if (NULL != wws)
   2168     GNUNET_NETWORK_fdset_destroy (wws);
   2169 }
   2170 
   2171 
   2172 /**
   2173  * Task run whenever HTTP server operations are pending.
   2174  *
   2175  * @param cls NULL
   2176  */
   2177 static void
   2178 do_httpd (void *cls)
   2179 {
   2180   (void) cls;
   2181   httpd_task = NULL;
   2182   MHD_run (mhd_daemon);
   2183   schedule_httpd ();
   2184 }
   2185 
   2186 
   2187 /**
   2188  * Run MHD now, we have extra data ready for the callback.
   2189  */
   2190 static void
   2191 run_mhd_now (void)
   2192 {
   2193   if (NULL != httpd_task)
   2194     GNUNET_SCHEDULER_cancel (httpd_task);
   2195   httpd_task = GNUNET_SCHEDULER_add_now (&do_httpd,
   2196                                          NULL);
   2197 }
   2198 
   2199 
   2200 /* *************** General / main code *************** */
   2201 
   2202 
   2203 /**
   2204  * Task run on shutdown
   2205  *
   2206  * @param cls closure
   2207  */
   2208 static void
   2209 do_shutdown (void *cls)
   2210 {
   2211   (void) cls;
   2212   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2213               "Shutting down...\n");
   2214   /* MHD requires resuming before destroying the daemons */
   2215   for (struct HttpRequest *hr = hr_head;
   2216        NULL != hr;
   2217        hr = hr->next)
   2218   {
   2219     if (GNUNET_YES == hr->suspended)
   2220     {
   2221       hr->suspended = GNUNET_NO;
   2222       MHD_resume_connection (hr->con);
   2223     }
   2224   }
   2225   kill_httpd ();
   2226   if (NULL != curl_multi)
   2227   {
   2228     curl_multi_cleanup (curl_multi);
   2229     curl_multi = NULL;
   2230   }
   2231   if (NULL != curl_download_task)
   2232   {
   2233     GNUNET_SCHEDULER_cancel (curl_download_task);
   2234     curl_download_task = NULL;
   2235   }
   2236   GNUNET_free (target_server_base_url);
   2237   GNUNET_free (delete_path);
   2238   GNUNET_free (flip_path_dl);
   2239 }
   2240 
   2241 
   2242 /**
   2243  * Connect to a unix domain socket.
   2244  *
   2245  * @param path the IPC path
   2246  * @param mode the IPC path mode
   2247  * @return the file descriptor of the connection.
   2248  */
   2249 static int
   2250 open_unix_path (const char *path,
   2251                 mode_t mode)
   2252 {
   2253 
   2254   struct GNUNET_NETWORK_Handle *nh;
   2255   struct sockaddr_un *un;
   2256   int fd;
   2257 
   2258   if (sizeof (un->sun_path) <= strlen (path))
   2259   {
   2260     fprintf (stderr,
   2261              "path `%s' too long\n",
   2262              path);
   2263     return -1;
   2264   }
   2265   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2266               "Creating listen socket '%s' with mode %o\n",
   2267               path,
   2268               mode);
   2269 
   2270   if (GNUNET_OK !=
   2271       GNUNET_DISK_directory_create_for_file (path))
   2272   {
   2273     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
   2274                               "mkdir",
   2275                               path);
   2276   }
   2277 
   2278   un = GNUNET_new (struct sockaddr_un);
   2279   un->sun_family = AF_UNIX;
   2280 
   2281   strncpy (un->sun_path,
   2282            path,
   2283            sizeof (un->sun_path) - 1);
   2284   GNUNET_NETWORK_unix_precheck (un);
   2285 
   2286   if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX,
   2287                                                   SOCK_STREAM,
   2288                                                   0)))
   2289   {
   2290     fprintf (stderr,
   2291              "create failed for AF_UNIX\n");
   2292     GNUNET_free (un);
   2293     return -1;
   2294   }
   2295   if (GNUNET_OK !=
   2296       GNUNET_NETWORK_socket_bind (nh,
   2297                                   (void *) un,
   2298                                   sizeof (struct sockaddr_un)))
   2299   {
   2300     fprintf (stderr,
   2301              "bind failed for AF_UNIX\n");
   2302     GNUNET_free (un);
   2303     GNUNET_NETWORK_socket_close (nh);
   2304     return -1;
   2305   }
   2306   GNUNET_free (un);
   2307   if (GNUNET_OK != GNUNET_NETWORK_socket_listen (nh,
   2308                                                  UNIX_BACKLOG))
   2309   {
   2310     fprintf (stderr,
   2311              "listen failed for AF_UNIX\n");
   2312     GNUNET_NETWORK_socket_close (nh);
   2313     return -1;
   2314   }
   2315 
   2316   if (0 != chmod (path,
   2317                   mode))
   2318   {
   2319     fprintf (stderr,
   2320              "chmod failed: %s\n",
   2321              strerror (errno));
   2322     GNUNET_NETWORK_socket_close (nh);
   2323     return -1;
   2324   }
   2325   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2326               "set socket '%s' to mode %o\n",
   2327               path,
   2328               mode);
   2329   fd = GNUNET_NETWORK_get_fd (nh);
   2330   GNUNET_NETWORK_socket_free_memory_only_ (nh);
   2331   return fd;
   2332 }
   2333 
   2334 
   2335 /**
   2336  * Crawl the configuration file and extracts the serving
   2337  * method, TCP vs IPC, and the respective details (port/path/mode)
   2338  *
   2339  * @param ccfg configuration handle.
   2340  * @param port[out] port number to use
   2341  * @param path[out] unix path for IPC.
   2342  * @param mode[out] mode string for @a path.
   2343  * @return #GNUNET_SYSERR if the parsing didn't succeed.
   2344  */
   2345 static int
   2346 parse_serving_mean (const struct GNUNET_CONFIGURATION_Handle *ccfg,
   2347                     uint16_t *port,
   2348                     char **path,
   2349                     mode_t *mode)
   2350 {
   2351 
   2352   const char *serve;
   2353   const char *choices[] = {"tcp", "unix", NULL};
   2354   char *modestring;
   2355   unsigned long long port_ull;
   2356 
   2357 
   2358   if (GNUNET_OK !=
   2359       GNUNET_CONFIGURATION_get_value_choice (ccfg,
   2360                                              "twister",
   2361                                              "SERVE",
   2362                                              choices,
   2363                                              &serve))
   2364   {
   2365     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2366                                "twister",
   2367                                "SERVE");
   2368     return GNUNET_SYSERR;
   2369   }
   2370 
   2371   if (0 == strcmp ("tcp", serve))
   2372   {
   2373     *path = NULL;
   2374 
   2375     if (GNUNET_OK !=
   2376         GNUNET_CONFIGURATION_get_value_number (ccfg,
   2377                                                "twister",
   2378                                                "HTTP_PORT",
   2379                                                &port_ull))
   2380     {
   2381       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2382                                  "twister",
   2383                                  "HTTP_PORT");
   2384       return GNUNET_SYSERR;
   2385     }
   2386     *port = (uint16_t) port_ull;
   2387     return GNUNET_OK;
   2388   }
   2389 
   2390   /* serving via unix */
   2391 
   2392   if (GNUNET_OK !=
   2393       GNUNET_CONFIGURATION_get_value_filename (ccfg,
   2394                                                "twister",
   2395                                                "SERVE_UNIXPATH",
   2396                                                path))
   2397   {
   2398     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2399                                "twister",
   2400                                "SERVE_UNIXPATH");
   2401     return GNUNET_SYSERR;
   2402   }
   2403 
   2404   if (GNUNET_OK !=
   2405       GNUNET_CONFIGURATION_get_value_string (ccfg,
   2406                                              "twister",
   2407                                              "SERVE_UNIXMODE",
   2408                                              &modestring))
   2409   {
   2410     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2411                                "twister",
   2412                                "SERVE_UNIXMODE");
   2413     return GNUNET_SYSERR;
   2414   }
   2415 
   2416   errno = 0;
   2417   *mode = (mode_t) strtoul (modestring, NULL, 8);
   2418 
   2419   if (0 != errno)
   2420   {
   2421     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2422                                "twister",
   2423                                "SERVE_UNIXMODE",
   2424                                "must be octal number");
   2425     GNUNET_free (modestring);
   2426     return GNUNET_SYSERR;
   2427   }
   2428   GNUNET_free (modestring);
   2429   return GNUNET_OK;
   2430 }
   2431 
   2432 
   2433 /**
   2434  * Main function that will be run.  Main tasks are (1) init. the
   2435  * curl infrastructure (curl_global_init() / curl_multi_init()),
   2436  * then fetch the HTTP port where its Web service should listen at,
   2437  * and finally start MHD on that port.
   2438  *
   2439  * @param cls closure
   2440  * @param c configuration
   2441  * @param service the initialized service
   2442 */
   2443 static void
   2444 run (void *cls,
   2445      const struct GNUNET_CONFIGURATION_Handle *c,
   2446      struct GNUNET_SERVICE_Handle *service)
   2447 {
   2448   uint16_t port = 0;
   2449   int fh = -1;
   2450   char *serve_unixpath = NULL;
   2451   mode_t serve_unixmode = 0;
   2452 
   2453   (void) cls;
   2454   (void) service;
   2455   cfg = c;
   2456 
   2457   if (0 != curl_global_init (CURL_GLOBAL_WIN32))
   2458   {
   2459     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2460                 "cURL global init failed!\n");
   2461     GNUNET_SCHEDULER_shutdown ();
   2462     return;
   2463   }
   2464   if (NULL == (curl_multi = curl_multi_init ()))
   2465   {
   2466     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2467                 "Failed to create cURL multi handle!\n");
   2468     return;
   2469   }
   2470 
   2471   /* No need to check return value.  If given, we take,
   2472    * otherwise it stays zero.  */
   2473   if (GNUNET_OK !=
   2474       GNUNET_CONFIGURATION_get_value_number (c,
   2475                                              "twister",
   2476                                              "CHAOS_RATE",
   2477                                              &chaos_rate))
   2478     chaos_rate = 0;
   2479   if (100 < chaos_rate)
   2480   {
   2481     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2482                                "twister",
   2483                                "CHAOS_RATE",
   2484                                "must be below 100");
   2485     GNUNET_SCHEDULER_shutdown ();
   2486     return;
   2487   }
   2488 
   2489   if (GNUNET_OK !=
   2490       GNUNET_CONFIGURATION_get_value_string
   2491         (c,
   2492         "twister",
   2493         "DESTINATION_BASE_URL",
   2494         &target_server_base_url))
   2495   {
   2496     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2497                                "twister",
   2498                                "DESTINATION_BASE_URL");
   2499     GNUNET_SCHEDULER_shutdown ();
   2500     return;
   2501   }
   2502 
   2503   if (GNUNET_SYSERR ==
   2504       parse_serving_mean (c,
   2505                           &port,
   2506                           &serve_unixpath,
   2507                           &serve_unixmode))
   2508   {
   2509     GNUNET_break (0);
   2510     GNUNET_SCHEDULER_shutdown ();
   2511     return;
   2512   }
   2513 
   2514   if (NULL != serve_unixpath)
   2515   {
   2516     /* Connect the 'fh' socket.  */
   2517     fh = open_unix_path (serve_unixpath,
   2518                          serve_unixmode);
   2519 
   2520     GNUNET_assert (-1 != fh);
   2521   }
   2522 
   2523   /* start MHD daemon for HTTP */
   2524   mhd_daemon = MHD_start_daemon
   2525                  (MHD_USE_DEBUG | MHD_ALLOW_SUSPEND_RESUME | MHD_USE_DUAL_STACK,
   2526                  (-1 == fh) ? (uint16_t) port : 0,
   2527                  NULL, NULL,
   2528                  &create_response, NULL,
   2529                  MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
   2530                  MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
   2531                  MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
   2532                  MHD_OPTION_LISTEN_SOCKET, fh,
   2533                  MHD_OPTION_END);
   2534 
   2535   if (NULL == mhd_daemon)
   2536   {
   2537     GNUNET_break (0);
   2538     GNUNET_SCHEDULER_shutdown ();
   2539     return;
   2540   }
   2541   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   2542                                  NULL);
   2543   run_mhd_now ();
   2544 }
   2545 
   2546 
   2547 /**
   2548  * Callback called when a client connects to the service.
   2549  *
   2550  * @param cls closure for the service
   2551  * @param c the new client that connected to the service
   2552  * @param mq the message queue used to send messages to the client
   2553  * @return @a c
   2554  */
   2555 static void *
   2556 client_connect_cb (void *cls,
   2557                    struct GNUNET_SERVICE_Client *c,
   2558                    struct GNUNET_MQ_Handle *mq)
   2559 {
   2560   (void) cls;
   2561   (void) mq;
   2562   return c;
   2563 }
   2564 
   2565 
   2566 /**
   2567  * Callback called when a client disconnected from the service
   2568  *
   2569  * @param cls closure for the service
   2570  * @param c the client that disconnected
   2571  * @param internal_cls should be equal to @a c
   2572  */
   2573 static void
   2574 client_disconnect_cb (void *cls,
   2575                       struct GNUNET_SERVICE_Client *c,
   2576                       void *internal_cls)
   2577 {
   2578   /* intentionally empty */
   2579   (void) cls;
   2580   (void) c;
   2581   (void) internal_cls;
   2582 }
   2583 
   2584 
   2585 /**
   2586  * Send confirmation that the operation was handled.
   2587  *
   2588  * @param c handle to the client waiting for confirmation.
   2589  */
   2590 static void
   2591 send_acknowledgement (struct GNUNET_SERVICE_Client *c)
   2592 {
   2593   struct GNUNET_MQ_Envelope *env;
   2594   struct GNUNET_MessageHeader *hdr;
   2595 
   2596   env = GNUNET_MQ_msg (hdr,
   2597                        TWISTER_MESSAGE_TYPE_ACKNOWLEDGEMENT);
   2598   GNUNET_MQ_send (GNUNET_SERVICE_client_get_mq (c),
   2599                   env);
   2600   GNUNET_SERVICE_client_continue (c);
   2601 }
   2602 
   2603 
   2604 /**
   2605  * Control handler for malforming responses.
   2606  *
   2607  * @param cls message queue for sending replies
   2608  * @param src received message
   2609  */
   2610 static void
   2611 handle_malform (void *cls,
   2612                 const struct TWISTER_Malform *src)
   2613 {
   2614   struct GNUNET_SERVICE_Client *c = cls;
   2615 
   2616   TWISTER_LOG_DEBUG ("Flagging response malformation\n");
   2617   malform = GNUNET_YES;
   2618   send_acknowledgement (c);
   2619 }
   2620 
   2621 
   2622 /**
   2623  * Control handler for malforming requests.
   2624  *
   2625  * @param cls message queue for sending replies
   2626  * @param src received message
   2627  */
   2628 static void
   2629 handle_malform_upload (void *cls,
   2630                        const struct TWISTER_Malform *src)
   2631 {
   2632   struct GNUNET_SERVICE_Client *c = cls;
   2633 
   2634   TWISTER_LOG_DEBUG ("Flagging request malformation\n");
   2635   malform_upload = GNUNET_YES;
   2636   send_acknowledgement (c);
   2637 }
   2638 
   2639 
   2640 /**
   2641  * Control handler for deleting JSON response objects
   2642  *
   2643  * @param cls message queue for sending replies
   2644  * @param src received message
   2645  */
   2646 static int
   2647 check_modify_path_dl (void *cls,
   2648                       const struct TWISTER_ModifyPath *src)
   2649 {
   2650   return GNUNET_OK;
   2651 }
   2652 
   2653 
   2654 /**
   2655  * Control handler for deleting JSON response objects
   2656  *
   2657  * @param cls message queue for sending replies
   2658  * @param src received message
   2659  */
   2660 static void
   2661 handle_modify_path_dl (void *cls,
   2662                        const struct TWISTER_ModifyPath *src)
   2663 {
   2664   struct GNUNET_SERVICE_Client *c = cls;
   2665   uint16_t tailsize;
   2666   char *payload_path;
   2667   char *payload_value;
   2668 
   2669   tailsize = ntohs (src->header.size) - sizeof (*src);
   2670 
   2671   if (tailsize != GNUNET_STRINGS_buffer_tokenize
   2672         ((const char *) &src[1],
   2673         tailsize,
   2674         2,
   2675         &payload_path,
   2676         &payload_value))
   2677   {
   2678     GNUNET_break_op (0);
   2679     GNUNET_SERVICE_client_drop (c);
   2680     return;
   2681   }
   2682 
   2683   modify_path_dl = GNUNET_strdup (payload_path);
   2684   modify_value = GNUNET_strdup (payload_value);
   2685 
   2686   send_acknowledgement (c);
   2687 }
   2688 
   2689 
   2690 /**
   2691  * Control handler for deleting JSON response objects
   2692  *
   2693  * @param cls message queue for sending replies
   2694  * @param src received message
   2695  */
   2696 static int
   2697 check_modify_path_ul (void *cls,
   2698                       const struct TWISTER_ModifyPath *src)
   2699 {
   2700   return GNUNET_OK;
   2701 }
   2702 
   2703 
   2704 /**
   2705  * Control handler for deleting JSON request objects;
   2706  * (means request to the proxied services)
   2707  *
   2708  * @param cls message queue for sending replies
   2709  * @param src received message
   2710  */
   2711 static void
   2712 handle_modify_path_ul (void *cls,
   2713                        const struct TWISTER_ModifyPath *src)
   2714 {
   2715   struct GNUNET_SERVICE_Client *c = cls;
   2716   uint16_t tailsize;
   2717   char *payload_path;
   2718   char *payload_value;
   2719 
   2720   tailsize = ntohs (src->header.size) - sizeof (*src);
   2721 
   2722   if (tailsize != GNUNET_STRINGS_buffer_tokenize
   2723         ((const char *) &src[1],
   2724         tailsize,
   2725         2,
   2726         &payload_path,
   2727         &payload_value))
   2728   {
   2729     GNUNET_break_op (0);
   2730     GNUNET_SERVICE_client_drop (c);
   2731     return;
   2732   }
   2733 
   2734   modify_path_dl = GNUNET_strdup (payload_path);
   2735   modify_value = GNUNET_strdup (payload_value);
   2736 
   2737   send_acknowledgement (c);
   2738 }
   2739 
   2740 
   2741 /**
   2742  * Control handler for changing an HTTP response header.
   2743  *
   2744  * @param cls message queue for sending replies
   2745  * @param src received message
   2746  */
   2747 static int
   2748 check_modify_header_dl (void *cls,
   2749                         const struct TWISTER_ModifyPath *src)
   2750 {
   2751   return GNUNET_OK;
   2752 }
   2753 
   2754 
   2755 /**
   2756  * Control handler for changing an HTTP response header.
   2757  *
   2758  * @param cls message queue for sending replies
   2759  * @param src received message
   2760  */
   2761 static void
   2762 handle_modify_header_dl (void *cls,
   2763                          const struct TWISTER_ModifyPath *src)
   2764 {
   2765   struct GNUNET_SERVICE_Client *c = cls;
   2766   uint16_t tailsize;
   2767   char *payload_path;
   2768   char *payload_value;
   2769 
   2770   tailsize = ntohs (src->header.size) - sizeof (*src);
   2771 
   2772   if (tailsize != GNUNET_STRINGS_buffer_tokenize
   2773         ((const char *) &src[1],
   2774         tailsize,
   2775         2,
   2776         &payload_path,
   2777         &payload_value))
   2778   {
   2779     GNUNET_break_op (0);
   2780     GNUNET_SERVICE_client_drop (c);
   2781     return;
   2782   }
   2783 
   2784   modify_header_dl = GNUNET_strdup (payload_path);
   2785   modify_value = GNUNET_strdup (payload_value);
   2786 
   2787   send_acknowledgement (c);
   2788 }
   2789 
   2790 
   2791 /**
   2792  * Control handler for flipping JSON strings into response objects
   2793  *
   2794  * @param cls message queue for sending replies
   2795  * @param src received message
   2796  */
   2797 static void
   2798 handle_flip_path_dl (void *cls,
   2799                      const struct TWISTER_FlipPath *src)
   2800 {
   2801   struct GNUNET_SERVICE_Client *c = cls;
   2802   uint16_t tailsize;
   2803   char *payload;
   2804 
   2805   tailsize = ntohs (src->header.size) - sizeof (*src);
   2806   if (tailsize !=
   2807       GNUNET_STRINGS_buffer_tokenize (
   2808         (const char *) &src[1],
   2809         tailsize,
   2810         1,
   2811         &payload))
   2812   {
   2813     GNUNET_break_op (0);
   2814     GNUNET_SERVICE_client_drop (c);
   2815     return;
   2816   }
   2817   GNUNET_free (flip_path_dl);
   2818   flip_path_dl = GNUNET_strdup (payload);
   2819   send_acknowledgement (c);
   2820 }
   2821 
   2822 
   2823 /**
   2824  * Control handler for flipping JSON strings into request objects
   2825  *
   2826  * @param cls message queue for sending replies
   2827  * @param src received message
   2828  */
   2829 static int
   2830 check_flip_path_ul (void *cls,
   2831                     const struct TWISTER_FlipPath *src)
   2832 {
   2833   return GNUNET_OK;
   2834 }
   2835 
   2836 
   2837 /**
   2838  * Control handler for flipping JSON strings into request objects
   2839  *
   2840  * @param cls message queue for sending replies
   2841  * @param src received message
   2842  */
   2843 static int
   2844 check_flip_path_dl (void *cls,
   2845                     const struct TWISTER_FlipPath *src)
   2846 {
   2847   return GNUNET_OK;
   2848 }
   2849 
   2850 
   2851 /**
   2852  * Control handler for flipping JSON strings into request objects
   2853  *
   2854  * @param cls message queue for sending replies
   2855  * @param src received message
   2856  */
   2857 static void
   2858 handle_flip_path_ul (void *cls,
   2859                      const struct TWISTER_FlipPath *src)
   2860 {
   2861   struct GNUNET_SERVICE_Client *c = cls;
   2862   uint16_t tailsize;
   2863   char *payload;
   2864 
   2865   tailsize = ntohs (src->header.size) - sizeof (*src);
   2866 
   2867   if (tailsize != GNUNET_STRINGS_buffer_tokenize
   2868         ((const char *) &src[1],
   2869         tailsize,
   2870         1,
   2871         &payload))
   2872   {
   2873     GNUNET_break_op (0);
   2874     GNUNET_SERVICE_client_drop (c);
   2875     return;
   2876   }
   2877 
   2878   flip_path_ul = GNUNET_strdup (payload);
   2879   send_acknowledgement (c);
   2880 }
   2881 
   2882 
   2883 /**
   2884  * Control handler for deleting JSON fields from response objects
   2885  *
   2886  * @param cls message queue for sending replies
   2887  * @param src received message
   2888  */
   2889 static int
   2890 check_delete_path (void *cls,
   2891                    const struct TWISTER_DeletePath *src)
   2892 {
   2893   return GNUNET_OK;
   2894 }
   2895 
   2896 
   2897 /**
   2898  * Control handler for deleting JSON fields from response objects
   2899  *
   2900  * @param cls message queue for sending replies
   2901  * @param src received message
   2902  */
   2903 static void
   2904 handle_delete_path (void *cls,
   2905                     const struct TWISTER_DeletePath *src)
   2906 {
   2907   struct GNUNET_SERVICE_Client *c = cls;
   2908   uint16_t tailsize;
   2909   char *payload;
   2910 
   2911   tailsize = ntohs (src->header.size) - sizeof (*src);
   2912 
   2913   if (tailsize != GNUNET_STRINGS_buffer_tokenize
   2914         ((const char *) &src[1],
   2915         tailsize,
   2916         1,
   2917         &payload))
   2918   {
   2919     GNUNET_break_op (0);
   2920     GNUNET_SERVICE_client_drop (c);
   2921     return;
   2922   }
   2923   GNUNET_free (delete_path);
   2924   delete_path = GNUNET_strdup (payload);
   2925 
   2926   send_acknowledgement (c);
   2927 }
   2928 
   2929 
   2930 /**
   2931  * Control handler for changing the response code
   2932  *
   2933  * @param cls message queue for sending replies
   2934  * @param src received message
   2935  */
   2936 static void
   2937 handle_set_response_code
   2938   (void *cls,
   2939   const struct TWISTER_SetResponseCode *src)
   2940 {
   2941   struct GNUNET_SERVICE_Client *c = cls;
   2942 
   2943   hack_response_code = ntohl (src->response_code);
   2944   send_acknowledgement (c);
   2945 }
   2946 
   2947 
   2948 /**
   2949  * Main function.
   2950  */
   2951 int
   2952 main (int argc,
   2953       char *const *argv)
   2954 {
   2955   struct GNUNET_MQ_MessageHandler mh[] = {
   2956     GNUNET_MQ_hd_fixed_size (set_response_code,
   2957                              TWISTER_MESSAGE_TYPE_SET_RESPONSE_CODE,
   2958                              struct TWISTER_SetResponseCode,
   2959                              NULL),
   2960     GNUNET_MQ_hd_var_size (modify_path_ul,
   2961                            TWISTER_MESSAGE_TYPE_MODIFY_PATH_UL,
   2962                            struct TWISTER_ModifyPath,
   2963                            NULL),
   2964     GNUNET_MQ_hd_var_size (modify_path_dl,
   2965                            TWISTER_MESSAGE_TYPE_MODIFY_PATH_DL,
   2966                            struct TWISTER_ModifyPath,
   2967                            NULL),
   2968     GNUNET_MQ_hd_var_size (modify_header_dl,
   2969                            TWISTER_MESSAGE_TYPE_MODIFY_HEADER_DL,
   2970                            struct TWISTER_ModifyPath,
   2971                            NULL),
   2972     GNUNET_MQ_hd_fixed_size (malform_upload,
   2973                              TWISTER_MESSAGE_TYPE_MALFORM_UPLOAD,
   2974                              struct TWISTER_Malform,
   2975                              NULL),
   2976     GNUNET_MQ_hd_fixed_size (malform,
   2977                              TWISTER_MESSAGE_TYPE_MALFORM,
   2978                              struct TWISTER_Malform,
   2979                              NULL),
   2980     GNUNET_MQ_hd_var_size (delete_path,
   2981                            TWISTER_MESSAGE_TYPE_DELETE_PATH,
   2982                            struct TWISTER_DeletePath,
   2983                            NULL),
   2984     GNUNET_MQ_hd_var_size (flip_path_ul,
   2985                            TWISTER_MESSAGE_TYPE_FLIP_PATH_UL,
   2986                            struct TWISTER_FlipPath,
   2987                            NULL),
   2988     GNUNET_MQ_hd_var_size (flip_path_dl,
   2989                            TWISTER_MESSAGE_TYPE_FLIP_PATH_DL,
   2990                            struct TWISTER_FlipPath,
   2991                            NULL),
   2992     GNUNET_MQ_handler_end ()
   2993   };
   2994 
   2995   return GNUNET_SERVICE_run_ (TWISTER_project_data (),
   2996                               argc,
   2997                               argv,
   2998                               "twister",
   2999                               GNUNET_SERVICE_OPTION_NONE,
   3000                               &run,
   3001                               &client_connect_cb,
   3002                               &client_disconnect_cb,
   3003                               NULL,
   3004                               mh);
   3005 }
   3006 
   3007 
   3008 /* end of taler-twister-service.c */