twister

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

taler-twister-service.c (78442B)


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