exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mhd_parsing.c (18297B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014--2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file mhd_parsing.c
     18  * @brief functions to parse incoming requests (MHD arguments and JSON snippets)
     19  * @author Florian Dold
     20  * @author Benedikt Mueller
     21  * @author Christian Grothoff
     22  */
     23 #include <gnunet/gnunet_util_lib.h>
     24 #include <gnunet/gnunet_json_lib.h>
     25 #include <gnunet/gnunet_mhd_lib.h>
     26 #include "taler/taler_json_lib.h"
     27 #include "taler/taler_mhd_lib.h"
     28 
     29 
     30 /**
     31  * Longest long-poll wait we grant a client, in milliseconds.  While
     32  * long-polling, the connection is suspended and thus not subject to
     33  * MHD's connection timeout, so without a bound a client could pin the
     34  * connection (with its file descriptor and database event handler)
     35  * for the lifetime of the process.  Clients simply poll again.
     36  */
     37 #define MAX_TIMEOUT_MS (30LLU * 60 * 1000)
     38 
     39 
     40 enum GNUNET_GenericReturnValue
     41 TALER_MHD_parse_post_json (struct MHD_Connection *connection,
     42                            void **con_cls,
     43                            const char *upload_data,
     44                            size_t *upload_data_size,
     45                            json_t **json)
     46 {
     47   enum GNUNET_MHD_PostResult pr;
     48 
     49   pr = GNUNET_MHD_post_parser (TALER_MHD_REQUEST_BUFFER_MAX,
     50                                connection,
     51                                con_cls,
     52                                upload_data,
     53                                upload_data_size,
     54                                json);
     55   switch (pr)
     56   {
     57   case GNUNET_MHD_PR_OUT_OF_MEMORY:
     58     GNUNET_break (NULL == *json);
     59     return (MHD_NO ==
     60             TALER_MHD_reply_with_error (
     61               connection,
     62               MHD_HTTP_INTERNAL_SERVER_ERROR,
     63               TALER_EC_GENERIC_PARSER_OUT_OF_MEMORY,
     64               NULL)) ? GNUNET_SYSERR : GNUNET_NO;
     65 
     66   case GNUNET_MHD_PR_CONTINUE:
     67     GNUNET_break (NULL == *json);
     68     return GNUNET_YES;
     69   case GNUNET_MHD_PR_REQUEST_TOO_LARGE:
     70     GNUNET_break (NULL == *json);
     71     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     72                 "Closing connection, upload too large\n");
     73     return GNUNET_SYSERR;
     74   case GNUNET_MHD_PR_JSON_INVALID:
     75     GNUNET_break (NULL == *json);
     76     return (MHD_YES ==
     77             TALER_MHD_reply_with_error (connection,
     78                                         MHD_HTTP_BAD_REQUEST,
     79                                         TALER_EC_GENERIC_JSON_INVALID,
     80                                         NULL))
     81            ? GNUNET_NO : GNUNET_SYSERR;
     82   case GNUNET_MHD_PR_SUCCESS:
     83     GNUNET_break (NULL != *json);
     84     return GNUNET_YES;
     85   }
     86   /* this should never happen */
     87   GNUNET_break (0);
     88   return GNUNET_SYSERR;
     89 }
     90 
     91 
     92 void
     93 TALER_MHD_parse_post_cleanup_callback (void *con_cls)
     94 {
     95   GNUNET_MHD_post_parser_cleanup (con_cls);
     96 }
     97 
     98 
     99 /**
    100  * Extract fixed-size base32crockford encoded data from request.
    101  *
    102  * Queues an error response to the connection if the parameter is missing or
    103  * invalid.
    104  *
    105  * @param connection the MHD connection
    106  * @param param_name the name of the HTTP key with the value
    107  * @param kind whether to extract from header, argument or footer
    108  * @param[out] out_data pointer to store the result
    109  * @param out_size expected size of @a out_data
    110  * @param[out] present set to true if argument was found
    111  * @return
    112  *   #GNUNET_YES if the the argument is present
    113  *   #GNUNET_NO if the argument is absent or malformed
    114  *   #GNUNET_SYSERR on internal error (error response could not be sent)
    115  */
    116 static enum GNUNET_GenericReturnValue
    117 parse_request_data (
    118   struct MHD_Connection *connection,
    119   const char *param_name,
    120   enum MHD_ValueKind kind,
    121   void *out_data,
    122   size_t out_size,
    123   bool *present)
    124 {
    125   const char *str;
    126 
    127   str = MHD_lookup_connection_value (connection,
    128                                      kind,
    129                                      param_name);
    130   if (NULL == str)
    131   {
    132     *present = false;
    133     return GNUNET_OK;
    134   }
    135   if (GNUNET_OK !=
    136       GNUNET_STRINGS_string_to_data (str,
    137                                      strlen (str),
    138                                      out_data,
    139                                      out_size))
    140   {
    141     GNUNET_break_op (0);
    142     return (MHD_NO ==
    143             TALER_MHD_reply_with_error (connection,
    144                                         MHD_HTTP_BAD_REQUEST,
    145                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
    146                                         param_name))
    147            ? GNUNET_SYSERR : GNUNET_NO;
    148   }
    149   *present = true;
    150   return GNUNET_OK;
    151 }
    152 
    153 
    154 enum GNUNET_GenericReturnValue
    155 TALER_MHD_parse_request_arg_data (
    156   struct MHD_Connection *connection,
    157   const char *param_name,
    158   void *out_data,
    159   size_t out_size,
    160   bool *present)
    161 {
    162   return parse_request_data (connection,
    163                              param_name,
    164                              MHD_GET_ARGUMENT_KIND,
    165                              out_data,
    166                              out_size,
    167                              present);
    168 }
    169 
    170 
    171 enum GNUNET_GenericReturnValue
    172 TALER_MHD_parse_request_header_data (
    173   struct MHD_Connection *connection,
    174   const char *header_name,
    175   void *out_data,
    176   size_t out_size,
    177   bool *present)
    178 {
    179   return parse_request_data (connection,
    180                              header_name,
    181                              MHD_HEADER_KIND,
    182                              out_data,
    183                              out_size,
    184                              present);
    185 }
    186 
    187 
    188 enum GNUNET_GenericReturnValue
    189 TALER_MHD_parse_request_arg_rel_time (
    190   struct MHD_Connection *connection,
    191   const char *label,
    192   struct GNUNET_TIME_Relative *duration)
    193 {
    194   const char *ts;
    195   char dummy;
    196   unsigned long long tms;
    197 
    198   ts = MHD_lookup_connection_value (connection,
    199                                     MHD_GET_ARGUMENT_KIND,
    200                                     label);
    201   if (NULL == ts)
    202   {
    203     *duration = GNUNET_TIME_UNIT_ZERO;
    204     return GNUNET_OK;
    205   }
    206   if (1 !=
    207       sscanf (ts,
    208               "%llu%c",
    209               &tms,
    210               &dummy))
    211   {
    212     enum MHD_Result mret;
    213 
    214     GNUNET_break_op (0);
    215     mret = TALER_MHD_reply_with_error (connection,
    216                                        MHD_HTTP_BAD_REQUEST,
    217                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    218                                        label);
    219     return (MHD_YES == mret)
    220       ? GNUNET_NO
    221       : GNUNET_SYSERR;
    222   }
    223   *duration = GNUNET_TIME_relative_saturating_multiply (
    224     GNUNET_TIME_UNIT_MILLISECONDS,
    225     tms);
    226   return GNUNET_OK;
    227 }
    228 
    229 
    230 enum GNUNET_GenericReturnValue
    231 TALER_MHD_parse_request_arg_timeout (
    232   struct MHD_Connection *connection,
    233   struct GNUNET_TIME_Absolute *expiration)
    234 {
    235   const char *ts;
    236   char dummy;
    237   unsigned long long tms;
    238 
    239   ts = MHD_lookup_connection_value (connection,
    240                                     MHD_GET_ARGUMENT_KIND,
    241                                     "timeout_ms");
    242   if (NULL == ts)
    243   {
    244     *expiration = GNUNET_TIME_UNIT_ZERO_ABS;
    245     return GNUNET_OK;
    246   }
    247   if (1 !=
    248       sscanf (ts,
    249               "%llu%c",
    250               &tms,
    251               &dummy))
    252   {
    253     enum MHD_Result mret;
    254 
    255     GNUNET_break_op (0);
    256     mret = TALER_MHD_reply_with_error (connection,
    257                                        MHD_HTTP_BAD_REQUEST,
    258                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    259                                        "timeout_ms");
    260     return (MHD_YES == mret)
    261       ? GNUNET_NO
    262       : GNUNET_SYSERR;
    263   }
    264   if (tms > MAX_TIMEOUT_MS)
    265     tms = MAX_TIMEOUT_MS;
    266   *expiration = GNUNET_TIME_relative_to_absolute (
    267     GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
    268                                    tms));
    269   return GNUNET_OK;
    270 }
    271 
    272 
    273 enum GNUNET_GenericReturnValue
    274 TALER_MHD_parse_request_arg_timestamp (
    275   struct MHD_Connection *connection,
    276   const char *fname,
    277   struct GNUNET_TIME_Timestamp *ts)
    278 {
    279   const char *s;
    280 
    281   s = MHD_lookup_connection_value (connection,
    282                                    MHD_GET_ARGUMENT_KIND,
    283                                    fname);
    284   if (NULL == s)
    285     return GNUNET_OK;
    286   if (GNUNET_OK !=
    287       GNUNET_STRINGS_fancy_time_to_timestamp (s,
    288                                               ts))
    289   {
    290     enum MHD_Result mret;
    291 
    292     GNUNET_break_op (0);
    293     mret = TALER_MHD_reply_with_error (
    294       connection,
    295       MHD_HTTP_BAD_REQUEST,
    296       TALER_EC_GENERIC_PARAMETER_MALFORMED,
    297       fname);
    298     return (MHD_YES == mret)
    299       ? GNUNET_NO
    300       : GNUNET_SYSERR;
    301   }
    302   return GNUNET_OK;
    303 }
    304 
    305 
    306 enum GNUNET_GenericReturnValue
    307 TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection,
    308                                     const char *name,
    309                                     uint64_t *off)
    310 {
    311   const char *ts;
    312   char dummy;
    313   unsigned long long num;
    314 
    315   ts = MHD_lookup_connection_value (connection,
    316                                     MHD_GET_ARGUMENT_KIND,
    317                                     name);
    318   if (NULL == ts)
    319     return GNUNET_OK;
    320   if (1 !=
    321       sscanf (ts,
    322               "%llu%c",
    323               &num,
    324               &dummy))
    325   {
    326     enum MHD_Result mret;
    327 
    328     GNUNET_break_op (0);
    329     mret = TALER_MHD_reply_with_error (connection,
    330                                        MHD_HTTP_BAD_REQUEST,
    331                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    332                                        name);
    333     return (MHD_YES == mret)
    334       ? GNUNET_NO
    335       : GNUNET_SYSERR;
    336   }
    337   *off = (uint64_t) num;
    338   return GNUNET_OK;
    339 }
    340 
    341 
    342 enum GNUNET_GenericReturnValue
    343 TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection,
    344                                      const char *name,
    345                                      int64_t *val)
    346 {
    347   const char *ts;
    348   char dummy;
    349   long long num;
    350 
    351   ts = MHD_lookup_connection_value (connection,
    352                                     MHD_GET_ARGUMENT_KIND,
    353                                     name);
    354   if (NULL == ts)
    355     return GNUNET_OK;
    356   if (1 !=
    357       sscanf (ts,
    358               "%lld%c",
    359               &num,
    360               &dummy))
    361   {
    362     enum MHD_Result mret;
    363 
    364     GNUNET_break_op (0);
    365     mret = TALER_MHD_reply_with_error (connection,
    366                                        MHD_HTTP_BAD_REQUEST,
    367                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    368                                        name);
    369     return (MHD_YES == mret)
    370       ? GNUNET_NO
    371       : GNUNET_SYSERR;
    372   }
    373   *val = (int64_t) num;
    374   return GNUNET_OK;
    375 }
    376 
    377 
    378 enum GNUNET_GenericReturnValue
    379 TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection,
    380                                     const char *name,
    381                                     struct TALER_Amount *val)
    382 {
    383   const char *ts;
    384 
    385   ts = MHD_lookup_connection_value (connection,
    386                                     MHD_GET_ARGUMENT_KIND,
    387                                     name);
    388   if (NULL == ts)
    389     return GNUNET_OK;
    390   if (GNUNET_OK !=
    391       TALER_string_to_amount (ts,
    392                               val))
    393   {
    394     enum MHD_Result mret;
    395 
    396     GNUNET_break_op (0);
    397     mret = TALER_MHD_reply_with_error (connection,
    398                                        MHD_HTTP_BAD_REQUEST,
    399                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    400                                        name);
    401     return (MHD_YES == mret)
    402       ? GNUNET_NO
    403       : GNUNET_SYSERR;
    404   }
    405   return GNUNET_OK;
    406 }
    407 
    408 
    409 enum GNUNET_GenericReturnValue
    410 TALER_MHD_parse_json_data (struct MHD_Connection *connection,
    411                            const json_t *root,
    412                            struct GNUNET_JSON_Specification *spec)
    413 {
    414   enum GNUNET_GenericReturnValue ret;
    415   const char *error_json_name;
    416   unsigned int error_line;
    417 
    418   ret = GNUNET_JSON_parse (root,
    419                            spec,
    420                            &error_json_name,
    421                            &error_line);
    422   if (GNUNET_SYSERR == ret)
    423   {
    424     if (NULL == error_json_name)
    425       error_json_name = "<no field>";
    426     ret = (MHD_YES ==
    427            TALER_MHD_REPLY_JSON_PACK (
    428              connection,
    429              MHD_HTTP_BAD_REQUEST,
    430              GNUNET_JSON_pack_string ("hint",
    431                                       TALER_ErrorCode_get_hint (
    432                                         TALER_EC_GENERIC_JSON_INVALID)),
    433              GNUNET_JSON_pack_uint64 ("code",
    434                                       TALER_EC_GENERIC_JSON_INVALID),
    435              GNUNET_JSON_pack_string ("field",
    436                                       error_json_name),
    437              GNUNET_JSON_pack_uint64 ("line",
    438                                       error_line)))
    439           ? GNUNET_NO : GNUNET_SYSERR;
    440     return ret;
    441   }
    442   return GNUNET_YES;
    443 }
    444 
    445 
    446 enum GNUNET_GenericReturnValue
    447 TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection,
    448                                     const json_t *root,
    449                                     struct GNUNET_JSON_Specification *spec)
    450 {
    451   enum GNUNET_GenericReturnValue ret;
    452   const char *error_json_name;
    453   unsigned int error_line;
    454 
    455   ret = GNUNET_JSON_parse (root,
    456                            spec,
    457                            &error_json_name,
    458                            &error_line);
    459   if (GNUNET_SYSERR == ret)
    460   {
    461     if (NULL == error_json_name)
    462       error_json_name = "<no field>";
    463     ret = (MHD_YES ==
    464            TALER_MHD_REPLY_JSON_PACK (
    465              connection,
    466              MHD_HTTP_INTERNAL_SERVER_ERROR,
    467              GNUNET_JSON_pack_string ("hint",
    468                                       TALER_ErrorCode_get_hint (
    469                                         TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)),
    470              GNUNET_JSON_pack_uint64 ("code",
    471                                       TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE),
    472              GNUNET_JSON_pack_string ("field",
    473                                       error_json_name),
    474              GNUNET_JSON_pack_uint64 ("line",
    475                                       error_line)))
    476           ? GNUNET_NO : GNUNET_SYSERR;
    477     return ret;
    478   }
    479   return GNUNET_YES;
    480 }
    481 
    482 
    483 enum GNUNET_GenericReturnValue
    484 TALER_MHD_parse_json_array (struct MHD_Connection *connection,
    485                             const json_t *root,
    486                             struct GNUNET_JSON_Specification *spec,
    487                             ...)
    488 {
    489   enum GNUNET_GenericReturnValue ret;
    490   const char *error_json_name;
    491   unsigned int error_line;
    492   va_list ap;
    493   json_int_t dim;
    494 
    495   va_start (ap, spec);
    496   dim = 0;
    497   while ( (-1 != (ret = va_arg (ap, int))) &&
    498           (NULL != root) )
    499   {
    500     dim++;
    501     root = json_array_get (root, ret);
    502   }
    503   va_end (ap);
    504   if (NULL == root)
    505   {
    506     ret = (MHD_YES ==
    507            TALER_MHD_REPLY_JSON_PACK (
    508              connection,
    509              MHD_HTTP_BAD_REQUEST,
    510              GNUNET_JSON_pack_string ("hint",
    511                                       TALER_ErrorCode_get_hint (
    512                                         TALER_EC_GENERIC_JSON_INVALID)),
    513              GNUNET_JSON_pack_uint64 ("code",
    514                                       TALER_EC_GENERIC_JSON_INVALID),
    515              GNUNET_JSON_pack_string ("detail",
    516                                       "expected array"),
    517              GNUNET_JSON_pack_uint64 ("dimension",
    518                                       dim)))
    519           ? GNUNET_NO : GNUNET_SYSERR;
    520     return ret;
    521   }
    522   ret = GNUNET_JSON_parse (root,
    523                            spec,
    524                            &error_json_name,
    525                            &error_line);
    526   if (GNUNET_SYSERR == ret)
    527   {
    528     if (NULL == error_json_name)
    529       error_json_name = "<no field>";
    530     ret = (MHD_YES ==
    531            TALER_MHD_REPLY_JSON_PACK (
    532              connection,
    533              MHD_HTTP_BAD_REQUEST,
    534              GNUNET_JSON_pack_string ("detail",
    535                                       error_json_name),
    536              GNUNET_JSON_pack_string ("hint",
    537                                       TALER_ErrorCode_get_hint (
    538                                         TALER_EC_GENERIC_JSON_INVALID)),
    539              GNUNET_JSON_pack_uint64 ("code",
    540                                       TALER_EC_GENERIC_JSON_INVALID),
    541              GNUNET_JSON_pack_uint64 ("line",
    542                                       error_line)))
    543           ? GNUNET_NO : GNUNET_SYSERR;
    544     return ret;
    545   }
    546   return GNUNET_YES;
    547 }
    548 
    549 
    550 enum GNUNET_GenericReturnValue
    551 TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
    552                                  unsigned long long max_len)
    553 {
    554   const char *cl;
    555   unsigned long long cv;
    556   char dummy;
    557 
    558   /* Maybe check for maximum upload size
    559        and refuse requests if they are just too big. */
    560   cl = MHD_lookup_connection_value (connection,
    561                                     MHD_HEADER_KIND,
    562                                     MHD_HTTP_HEADER_CONTENT_LENGTH);
    563   if (NULL == cl)
    564   {
    565     return GNUNET_OK;
    566 #if 0
    567     /* wallet currently doesn't always send content-length! */
    568     GNUNET_break_op (0);
    569     return (MHD_YES ==
    570             TALER_MHD_reply_with_error (connection,
    571                                         MHD_HTTP_BAD_REQUEST,
    572                                         TALER_EC_GENERIC_PARAMETER_MISSING,
    573                                         MHD_HTTP_HEADER_CONTENT_LENGTH))
    574       ? GNUNET_NO
    575       : GNUNET_SYSERR;
    576 #endif
    577   }
    578   if (1 != sscanf (cl,
    579                    "%llu%c",
    580                    &cv,
    581                    &dummy))
    582   {
    583     /* Not valid HTTP request, just close connection. */
    584     GNUNET_break_op (0);
    585     return (MHD_YES ==
    586             TALER_MHD_reply_with_error (connection,
    587                                         MHD_HTTP_BAD_REQUEST,
    588                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
    589                                         MHD_HTTP_HEADER_CONTENT_LENGTH))
    590       ? GNUNET_NO
    591       : GNUNET_SYSERR;
    592   }
    593   if (cv > max_len)
    594   {
    595     GNUNET_break_op (0);
    596     return (MHD_YES ==
    597             TALER_MHD_reply_request_too_large (connection))
    598     ? GNUNET_NO
    599     : GNUNET_SYSERR;
    600   }
    601   return GNUNET_OK;
    602 }
    603 
    604 
    605 /* end of mhd_parsing.c */