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 (17865B)


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