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


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