-rps service: prevent division by zero
[oweals/gnunet.git] / src / curl / curl.c
1 /*
2   This file is part of GNUnet
3   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
4
5   GNUnet is free software; you can redistribute it and/or modify it under the
6   terms of the GNU General Public License as published by the Free Software
7   Foundation; either version 3, or (at your option) any later version.
8
9   GNUnet 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 General Public License for more details.
12
13   You should have received a copy of the GNU General Public License along with
14   GNUnet; see the file COPYING.  If not, If not, see
15   <http://www.gnu.org/licenses/>
16 */
17 /**
18  * @file curl/curl.c
19  * @brief API for downloading JSON via CURL
20  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
21  * @author Christian Grothoff
22  */
23 #include "platform.h"
24 #if HAVE_CURL_CURL_H
25 #include <curl/curl.h>
26 #elif HAVE_GNURL_CURL_H
27 #include <gnurl/curl.h>
28 #endif
29 #include <jansson.h>
30 #include "gnunet_curl_lib.h"
31
32
33 /**
34  * Log error related to CURL operations.
35  *
36  * @param type log level
37  * @param function which function failed to run
38  * @param code what was the curl error code
39  */
40 #define CURL_STRERROR(type, function, code)      \
41  GNUNET_log (type,                               \
42              "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
43              function, __FILE__, __LINE__, curl_easy_strerror (code));
44
45 /**
46  * Print JSON parsing related error information
47  */
48 #define JSON_WARN(error)                                                \
49     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,                              \
50                 "JSON parsing failed at %s:%u: %s (%s)\n",              \
51                 __FILE__, __LINE__, error.text, error.source)
52
53
54 /**
55  * Failsafe flag. Raised if our constructor fails to initialize
56  * the Curl library.
57  */
58 static int curl_fail;
59
60
61 /**
62  * @brief Buffer data structure we use to buffer the HTTP download
63  * before giving it to the JSON parser.
64  */
65 struct DownloadBuffer
66 {
67
68   /**
69    * Download buffer
70    */
71   void *buf;
72
73   /**
74    * The size of the download buffer
75    */
76   size_t buf_size;
77
78   /**
79    * Error code (based on libc errno) if we failed to download
80    * (i.e. response too large).
81    */
82   int eno;
83
84 };
85
86
87 /**
88  * Jobs are CURL requests running within a `struct GNUNET_CURL_Context`.
89  */
90 struct GNUNET_CURL_Job
91 {
92
93   /**
94    * We keep jobs in a DLL.
95    */
96   struct GNUNET_CURL_Job *next;
97
98   /**
99    * We keep jobs in a DLL.
100    */
101   struct GNUNET_CURL_Job *prev;
102
103   /**
104    * Easy handle of the job.
105    */
106   CURL *easy_handle;
107
108   /**
109    * Context this job runs in.
110    */
111   struct GNUNET_CURL_Context *ctx;
112
113   /**
114    * Function to call upon completion.
115    */
116   GNUNET_CURL_JobCompletionCallback jcc;
117
118   /**
119    * Closure for @e jcc.
120    */
121   void *jcc_cls;
122
123   /**
124    * Buffer for response received from CURL.
125    */
126   struct DownloadBuffer db;
127
128 };
129
130
131 /**
132  * Context
133  */
134 struct GNUNET_CURL_Context
135 {
136   /**
137    * Curl multi handle
138    */
139   CURLM *multi;
140
141   /**
142    * Curl share handle
143    */
144   CURLSH *share;
145
146   /**
147    * We keep jobs in a DLL.
148    */
149   struct GNUNET_CURL_Job *jobs_head;
150
151   /**
152    * We keep jobs in a DLL.
153    */
154   struct GNUNET_CURL_Job *jobs_tail;
155
156   /**
157    * HTTP header "application/json", created once and used
158    * for all requests that need it.
159    */
160   struct curl_slist *json_header;
161
162   /**
163    * Function we need to call whenever the event loop's
164    * socket set changed.
165    */
166   GNUNET_CURL_RescheduleCallback cb;
167
168   /**
169    * Closure for @e cb.
170    */
171   void *cb_cls;
172 };
173
174
175 /**
176  * Initialise this library.  This function should be called before using any of
177  * the following functions.
178  *
179  * @param cb function to call when rescheduling is required
180  * @param cb_cls closure for @a cb
181  * @return library context
182  */
183 struct GNUNET_CURL_Context *
184 GNUNET_CURL_init (GNUNET_CURL_RescheduleCallback cb,
185                   void *cb_cls)
186 {
187   struct GNUNET_CURL_Context *ctx;
188   CURLM *multi;
189   CURLSH *share;
190
191   if (curl_fail)
192   {
193     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
194                 "Curl was not initialised properly\n");
195     return NULL;
196   }
197   if (NULL == (multi = curl_multi_init ()))
198   {
199     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
200                 "Failed to create a Curl multi handle\n");
201     return NULL;
202   }
203   if (NULL == (share = curl_share_init ()))
204   {
205     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
206                 "Failed to create a Curl share handle\n");
207     return NULL;
208   }
209   ctx = GNUNET_new (struct GNUNET_CURL_Context);
210   ctx->cb = cb;
211   ctx->cb_cls = cb_cls;
212   ctx->multi = multi;
213   ctx->share = share;
214   GNUNET_assert (NULL != (ctx->json_header =
215                           curl_slist_append (NULL,
216                                              "Content-Type: application/json")));
217   return ctx;
218 }
219
220
221 /**
222  * Callback used when downloading the reply to an HTTP request.
223  * Just appends all of the data to the `buf` in the
224  * `struct DownloadBuffer` for further processing. The size of
225  * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
226  * the download exceeds this size, we abort with an error.
227  *
228  * @param bufptr data downloaded via HTTP
229  * @param size size of an item in @a bufptr
230  * @param nitems number of items in @a bufptr
231  * @param cls the `struct DownloadBuffer`
232  * @return number of bytes processed from @a bufptr
233  */
234 static size_t
235 download_cb (char *bufptr,
236              size_t size,
237              size_t nitems,
238              void *cls)
239 {
240   struct DownloadBuffer *db = cls;
241   size_t msize;
242   void *buf;
243
244   if (0 == size * nitems)
245   {
246     /* Nothing (left) to do */
247     return 0;
248   }
249   msize = size * nitems;
250   if ( (msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
251   {
252     db->eno = ENOMEM;
253     return 0; /* signals an error to curl */
254   }
255   db->buf = GNUNET_realloc (db->buf,
256                             db->buf_size + msize);
257   buf = db->buf + db->buf_size;
258   GNUNET_memcpy (buf, bufptr, msize);
259   db->buf_size += msize;
260   return msize;
261 }
262
263
264 /**
265  * Schedule a CURL request to be executed and call the given @a jcc
266  * upon its completion.  Note that the context will make use of the
267  * CURLOPT_PRIVATE facility of the CURL @a eh.
268  *
269  * This function modifies the CURL handle to add the
270  * "Content-Type: application/json" header if @a add_json is set.
271  *
272  * @param ctx context to execute the job in
273  * @param eh curl easy handle for the request, will
274  *           be executed AND cleaned up
275  * @param add_json add "application/json" content type header
276  * @param jcc callback to invoke upon completion
277  * @param jcc_cls closure for @a jcc
278  * @return NULL on error (in this case, @eh is still released!)
279  */
280 struct GNUNET_CURL_Job *
281 GNUNET_CURL_job_add (struct GNUNET_CURL_Context *ctx,
282                      CURL *eh,
283                      int add_json,
284                      GNUNET_CURL_JobCompletionCallback jcc,
285                      void *jcc_cls)
286 {
287   struct GNUNET_CURL_Job *job;
288
289   if (GNUNET_YES == add_json)
290     if (CURLE_OK !=
291         curl_easy_setopt (eh,
292                           CURLOPT_HTTPHEADER,
293                           ctx->json_header))
294     {
295       GNUNET_break (0);
296       curl_easy_cleanup (eh);
297       return NULL;
298     }
299
300   job = GNUNET_new (struct GNUNET_CURL_Job);
301   if ( (CURLE_OK !=
302         curl_easy_setopt (eh,
303                           CURLOPT_PRIVATE,
304                           job)) ||
305        (CURLE_OK !=
306         curl_easy_setopt (eh,
307                           CURLOPT_WRITEFUNCTION,
308                           &download_cb)) ||
309        (CURLE_OK !=
310         curl_easy_setopt (eh,
311                           CURLOPT_WRITEDATA,
312                           &job->db)) ||
313        (CURLE_OK !=
314         curl_easy_setopt (eh,
315                           CURLOPT_SHARE,
316                           ctx->share)) ||
317        (CURLM_OK !=
318         curl_multi_add_handle (ctx->multi,
319                                eh)) )
320   {
321     GNUNET_break (0);
322     GNUNET_free (job);
323     curl_easy_cleanup (eh);
324     return NULL;
325   }
326
327   job->easy_handle = eh;
328   job->ctx = ctx;
329   job->jcc = jcc;
330   job->jcc_cls = jcc_cls;
331   GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
332                                ctx->jobs_tail,
333                                job);
334   ctx->cb (ctx->cb_cls);
335   return job;
336 }
337
338
339 /**
340  * Cancel a job.  Must only be called before the job completion
341  * callback is called for the respective job.
342  *
343  * @param job job to cancel
344  */
345 void
346 GNUNET_CURL_job_cancel (struct GNUNET_CURL_Job *job)
347 {
348   struct GNUNET_CURL_Context *ctx = job->ctx;
349
350   GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
351                                ctx->jobs_tail,
352                                job);
353   GNUNET_break (CURLM_OK ==
354                 curl_multi_remove_handle (ctx->multi,
355                                           job->easy_handle));
356   curl_easy_cleanup (job->easy_handle);
357   GNUNET_free_non_null (job->db.buf);
358   GNUNET_free (job);
359 }
360
361
362 /**
363  * Obtain information about the final result about the
364  * HTTP download. If the download was successful, parses
365  * the JSON in the @a db and returns it. Also returns
366  * the HTTP @a response_code.  If the download failed,
367  * the return value is NULL.  The response code is set
368  * in any case, on download errors to zero.
369  *
370  * Calling this function also cleans up @a db.
371  *
372  * @param db download buffer
373  * @param eh CURL handle (to get the response code)
374  * @param[out] response_code set to the HTTP response code
375  *             (or zero if we aborted the download, i.e.
376  *              because the response was too big, or if
377  *              the JSON we received was malformed).
378  * @return NULL if downloading a JSON reply failed
379  */
380 static json_t *
381 download_get_result (struct DownloadBuffer *db,
382                      CURL *eh,
383                      long *response_code)
384 {
385   json_t *json;
386   json_error_t error;
387   char *ct;
388
389   if ( (CURLE_OK !=
390         curl_easy_getinfo (eh,
391                            CURLINFO_CONTENT_TYPE,
392                            &ct)) ||
393        (NULL == ct) ||
394        (0 != strcasecmp (ct,
395                          "application/json")) )
396   {
397     /* No content type or explicitly not JSON, refuse to parse
398        (but keep response code) */
399     if (CURLE_OK !=
400         curl_easy_getinfo (eh,
401                            CURLINFO_RESPONSE_CODE,
402                            response_code))
403     {
404       /* unexpected error... */
405       GNUNET_break (0);
406       *response_code = 0;
407     }
408     return NULL;
409   }
410
411   json = NULL;
412   if (0 == db->eno)
413   {
414     json = json_loadb (db->buf,
415                        db->buf_size,
416                        JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
417                        &error);
418     if (NULL == json)
419     {
420       JSON_WARN (error);
421       *response_code = 0;
422     }
423   }
424   GNUNET_free_non_null (db->buf);
425   db->buf = NULL;
426   db->buf_size = 0;
427   if (NULL != json)
428   {
429     if (CURLE_OK !=
430         curl_easy_getinfo (eh,
431                            CURLINFO_RESPONSE_CODE,
432                            response_code))
433     {
434       /* unexpected error... */
435       GNUNET_break (0);
436       *response_code = 0;
437     }
438   }
439   return json;
440 }
441
442
443 /**
444  * Run the main event loop for the Taler interaction.
445  *
446  * @param ctx the library context
447  */
448 void
449 GNUNET_CURL_perform (struct GNUNET_CURL_Context *ctx)
450 {
451   CURLMsg *cmsg;
452   struct GNUNET_CURL_Job *job;
453   int n_running;
454   int n_completed;
455   long response_code;
456   json_t *j;
457
458   (void) curl_multi_perform (ctx->multi,
459                              &n_running);
460   while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
461                                                &n_completed)))
462   {
463     /* Only documented return value is CURLMSG_DONE */
464     GNUNET_break (CURLMSG_DONE == cmsg->msg);
465     GNUNET_assert (CURLE_OK ==
466                    curl_easy_getinfo (cmsg->easy_handle,
467                                       CURLINFO_PRIVATE,
468                                       (char **) &job));
469     GNUNET_assert (job->ctx == ctx);
470     j = download_get_result (&job->db,
471                              job->easy_handle,
472                              &response_code);
473     job->jcc (job->jcc_cls,
474               response_code,
475               j);
476     json_decref (j);
477     GNUNET_CURL_job_cancel (job);
478   }
479 }
480
481
482 /**
483  * Obtain the information for a select() call to wait until
484  * #GNUNET_CURL_perform() is ready again.  Note that calling
485  * any other GNUNET_CURL-API may also imply that the library
486  * is again ready for #GNUNET_CURL_perform().
487  *
488  * Basically, a client should use this API to prepare for select(),
489  * then block on select(), then call #GNUNET_CURL_perform() and then
490  * start again until the work with the context is done.
491  *
492  * This function will NOT zero out the sets and assumes that @a max_fd
493  * and @a timeout are already set to minimal applicable values.  It is
494  * safe to give this API FD-sets and @a max_fd and @a timeout that are
495  * already initialized to some other descriptors that need to go into
496  * the select() call.
497  *
498  * @param ctx context to get the event loop information for
499  * @param read_fd_set will be set for any pending read operations
500  * @param write_fd_set will be set for any pending write operations
501  * @param except_fd_set is here because curl_multi_fdset() has this argument
502  * @param max_fd set to the highest FD included in any set;
503  *        if the existing sets have no FDs in it, the initial
504  *        value should be "-1". (Note that `max_fd + 1` will need
505  *        to be passed to select().)
506  * @param timeout set to the timeout in milliseconds (!); -1 means
507  *        no timeout (NULL, blocking forever is OK), 0 means to
508  *        proceed immediately with #GNUNET_CURL_perform().
509  */
510 void
511 GNUNET_CURL_get_select_info (struct GNUNET_CURL_Context *ctx,
512                              fd_set *read_fd_set,
513                              fd_set *write_fd_set,
514                              fd_set *except_fd_set,
515                              int *max_fd,
516                              long *timeout)
517 {
518   long to;
519   int m;
520
521   m = -1;
522   GNUNET_assert (CURLM_OK ==
523                  curl_multi_fdset (ctx->multi,
524                                    read_fd_set,
525                                    write_fd_set,
526                                    except_fd_set,
527                                    &m));
528   to = *timeout;
529   *max_fd = GNUNET_MAX (m, *max_fd);
530   GNUNET_assert (CURLM_OK ==
531                  curl_multi_timeout (ctx->multi,
532                                      &to));
533
534   /* Only if what we got back from curl is smaller than what we
535      already had (-1 == infinity!), then update timeout */
536   if ( (to < *timeout) &&
537        (-1 != to) )
538     *timeout = to;
539   if ( (-1 == (*timeout)) &&
540        (NULL != ctx->jobs_head) )
541     *timeout = to;
542 }
543
544
545 /**
546  * Cleanup library initialisation resources.  This function should be called
547  * after using this library to cleanup the resources occupied during library's
548  * initialisation.
549  *
550  * @param ctx the library context
551  */
552 void
553 GNUNET_CURL_fini (struct GNUNET_CURL_Context *ctx)
554 {
555   /* all jobs must have been cancelled at this time, assert this */
556   GNUNET_assert (NULL == ctx->jobs_head);
557   curl_share_cleanup (ctx->share);
558   curl_multi_cleanup (ctx->multi);
559   curl_slist_free_all (ctx->json_header);
560   GNUNET_free (ctx);
561 }
562
563
564 /**
565  * Initial global setup logic, specifically runs the Curl setup.
566  */
567 __attribute__ ((constructor))
568 void
569 GNUNET_CURL_constructor__ (void)
570 {
571   CURLcode ret;
572
573   if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
574   {
575     CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
576                    "curl_global_init",
577                    ret);
578     curl_fail = 1;
579   }
580 }
581
582
583 /**
584  * Cleans up after us, specifically runs the Curl cleanup.
585  */
586 __attribute__ ((destructor))
587 void
588 GNUNET_CURL_destructor__ (void)
589 {
590   if (curl_fail)
591     return;
592   curl_global_cleanup ();
593 }
594
595 /* end of curl.c */