Asterisk - The Open Source Telephony Project  21.4.1
curl_utils.h
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 2023, Sangoma Technologies Corporation
5  *
6  * George Joseph <gjoseph@sangoma.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 #ifndef _CURL_UTILS_H
20 #define _CURL_UTILS_H
21 
22 #include <curl/curl.h>
23 #include "asterisk/acl.h"
24 
25 #define AST_CURL_DEFAULT_MAX_HEADER_LEN 2048
26 
27 #ifndef CURL_WRITEFUNC_ERROR
28 #define CURL_WRITEFUNC_ERROR 0
29 #endif
30 
31 /*! \defgroup curl_wrappers CURL Convenience Wrappers
32  * @{
33 
34 \section Overwiew Overview
35 
36 While libcurl is extremely flexible in what it allows you to do,
37 that flexibility comes at complexity price. The convenience wrappers
38 defined here aim to take away some of that complexity for run-of-the-mill
39 requests.
40 
41 \par A Basic Example
42 
43 If all you need to do is receive a document into a buffer...
44 
45 \code
46  char *url = "https://someurl";
47  size_t returned_length;
48  char *returned_data = NULL;
49 
50  long rc = ast_curler_simple(url, &returned_length, &returned_data, NULL);
51 
52  ast_log(LOG_ERROR, "rc: %ld size: %zu doc: %.*s \n",
53  rc, returned_length,
54  (int)returned_length, returned_data);
55  ast_free(returned_data);
56 \endcode
57 
58 If you need the headers as well...
59 
60 \code
61  char *url = "https://someurl";
62  size_t returned_length;
63  char *returned_data = NULL;
64  struct ast_variable *headers;
65 
66  long rc = ast_curler_simple(url, &returned_length, &returned_data,
67  &headers);
68 
69  ast_log(LOG_ERROR, "rc: %ld size: %zu doc: %.*s \n",
70  rc, returned_length,
71  (int)returned_length, returned_data);
72 
73  ast_free(returned_data);
74  ast_variables_destroy(headers);
75 \endcode
76 
77 \par A More Complex Example
78 
79 If you need more control, you can specify callbacks to capture
80 the response headers, do something other than write the data
81 to a memory buffer, or do some special socket manipulation like
82 check that the server's IP address matched an acl.
83 
84 Let's write the data to a file, capture the headers,
85 and make sure the server's IP address is whitelisted.
86 
87 The default callbacks can do that so all we need to do is
88 supply the data.
89 
90 \code
91  char *url = "http://something";
92 
93  struct ast_curl_write_data data = {
94  .output = fopen("myfile.txt", "w");
95  .debug_info = url,
96  };
97  struct ast_curl_header_data hdata = {
98  .debug_info = url,
99  };
100  struct ast_curl_open_socket_data osdata = {
101  .acl = my_acl_list,
102  .debug_info = url,
103  };
104  struct ast_curl_optional_data opdata = {
105  .open_socket_cb = ast_curl_open_socket_cb,
106  .open_socket_data = &osdata,
107  };
108 
109  long rc = ast_curler(url, 0, ast_curl_write_default_cb, &data,
110  ast_curl_header_default_cb, &hdata, &opdata);
111 
112  fclose(data.output);
113  ast_variables_destroy(hdata.headers);
114 
115 \endcode
116 
117 If you need even more control, you can supply your own
118 callbacks as well. This is a silly example of providing
119 your own write callback. It's basically what
120 ast_curler_write_to_file() does.
121 
122 \code
123 static size_t my_write_cb(char *data, size_t size,
124  size_t nmemb, void *client_data)
125 {
126  FILE *fp = (FILE *)client_data;
127  return fwrite(data, size, nmemb, fp);
128 }
129 
130 static long myfunc(char *url, char *file)
131 {
132  FILE *fp = fopen(file, "w");
133  long rc = ast_curler(url, 0, my_write_cb, fp, NULL, NULL, NULL);
134  fclose(fp);
135  return rc;
136 }
137 \endcode
138  */
139 
140 /*!
141  * \defgroup HeaderCallback Header Callback
142  * \ingroup curl_wrappers
143  * @{
144  *
145  * If you need to access the headers returned on the response,
146  * you can define a callback that curl will call for every
147  * header it receives.
148  *
149  * Your callback must follow the specification defined for
150  * CURLOPT_HEADERFUNCTION and implement the curl_write_callback
151  * prototype.
152  *
153  * The following ast_curl_headers objects compose a default
154  * implementation that will accumulate the headers in an
155  * ast_variable list.
156  */
157 
158 /*!
159  *
160  * \brief Context structure passed to \ref ast_curl_header_default_cb
161  *
162  */
164  /*!
165  * curl's default max header length is 100k but we rarely
166  * need that much. It's also possible that a malicious remote
167  * server could send tons of 100k headers in an attempt to
168  * cause an out-of-memory condition. Setting this value
169  * will cause us to simply ignore any header with a length
170  * that exceeds it. If not set, the length defined in
171  * #AST_CURL_DEFAULT_MAX_HEADER_LEN will be used.
172  */
174  /*!
175  * Identifying info placed at the start of log and trace messages.
176  */
177  char *debug_info;
178  /*!
179  * This list will contain all the headers received.
180  * \note curl converts all header names to lower case.
181  */
183  /*!
184  * \internal
185  * Private flag used to keep track of whether we're
186  * capturing headers or not. We only want them after
187  * we've seen an HTTP response code in the 2XX range
188  * and before the blank line that separaes the headers
189  * from the body.
190  */
191  int _capture;
192 };
193 
194 /*!
195  * \brief A default implementation of a header callback.
196  *
197  * This is an implementation of #CURLOPT_HEADERFUNCTION that performs
198  * basic sanity checks and saves headers in the
199  * ast_curl_header_data.headers ast_variable list.
200  *
201  * The curl prototype for this function is \ref curl_write_callback
202  *
203  * \warning If you decide to write your own callback, curl doesn't
204  * guarantee a terminating NULL in data passed to the callbacks!
205  *
206  * \param data Will contain a header line that may not be NULL terminated.
207  * \param size Always 1.
208  * \param nitems The number of bytes in data.
209  * \param client_data A pointer to whatever structure you passed to
210  * \ref ast_curler in the \p curl_header_data parameter.
211  *
212  * \return Number of bytes handled. Must be (size * nitems) or an
213  * error is signalled.
214  */
215 size_t curl_header_cb(char *data, size_t size,
216  size_t nitems, void *client_data);
217 
218 void curl_header_data_free(void *obj);
219 
220 /*!
221  * @}
222  */
223 
224 /*!
225  * \defgroup DataCallback Received Data Callback
226  * \ingroup curl_wrappers
227  * @{
228  *
229  * If you need to do something with the data received other than
230  * save it in a memory buffer, you can define a callback that curl
231  * will call for each "chunk" of data it receives from the server.
232  *
233  * Your callback must follow the specification defined for
234  * CURLOPT_WRITEFUNCTION and implement the 'curl_write_callback'
235  * prototype.
236  *
237  * The following ast_curl_write objects compose a default
238  * implementation that will write the data to any FILE *
239  * descriptor you choose.
240  */
241 
242 /*!
243  * \brief Context structure passed to \ref ast_curl_write_default_cb.
244  */
246  /*!
247  * If this value is > 0, the request will be cancelled when
248  * \a bytes_downloaded exceeds it.
249  */
251  /*!
252  * Where to write to. Could be anything you can get a FILE* for.
253  * A file opened with fopen, a buffer opened with open_memstream(), etc.
254  * Required by \ref ast_curl_write_default_cb.
255  */
256  FILE *output;
257  /*!
258  * Identifying info placed at the start of log and trace messages.
259  */
260  char *debug_info;
261  /*!
262  * Keeps track of the number of bytes read so far.
263  * This is updated by the callback regardless of
264  * whether the output stream is updating
265  * \ref stream_bytes_downloaded.
266  */
268  /*!
269  * A buffer to be used for anything the output stream needs.
270  * For instance, the address of this member can be passed to
271  * open_memstream which will update it as it reads data. When
272  * the memstream is flushed/closed, this will contain all of
273  * the data read so far. You must free this yourself with
274  * ast_std_free().
275  */
277  /*!
278  * Keeps track of the number of bytes read so far.
279  * Can be used by memstream.
280  */
282  /*!
283  * \internal
284  * Set if we automatically opened a memstream
285  */
286  int _internal_memstream;
287 };
288 
289 /*!
290  * \brief A default implementation of a write data callback.
291 
292  * This is a default implementation of the function described
293  * by CURLOPT_WRITEFUNCTION that writes data received to a
294  * user-provided FILE *. This function is called by curl itself
295  * when it determines it has enough data to warrant a write.
296  * This may be influenced by the value of
297  * ast_curl_optional_data.per_write_buffer_size.
298  * See the CURLOPT_WRITEFUNCTION documentation for more info.
299  *
300  * The curl prototype for this function is 'curl_write_callback'
301  *
302  * \param data Data read by curl.
303  * \param size Always 1.
304  * \param nitems The number of bytes read.
305  * \param client_data A pointer to whatever structure you passed to
306  * \ref ast_curler in the \p curl_write_data parameter.
307  *
308  * \return Number of bytes handled. Must be (size * nitems) or an
309  * error is signalled.
310  */
311 size_t curl_write_cb(char *data, size_t size, size_t nmemb, void *clientp);
312 
313 void curl_write_data_free(void *obj);
314 
315 /*!
316  * @}
317  */
318 
319 /*!
320  * \defgroup OpenSocket Open Socket Callback
321  * \ingroup curl_wrappers
322  * @{
323  *
324  * If you need to allocate the socket curl uses to make the
325  * request yourself or you need to do some checking on the
326  * request's resolved IP address, this is the callback for you.
327  *
328  * Your callback must follow the specification defined for
329  * CURLOPT_OPENSOCKETFUNCTION and implement the
330  * 'curl_opensocket_callback' prototype.
331  *
332  * The following ast_open_socket objects compose a default
333  * implementation that will not allow requests to servers
334  * not whitelisted in the provided ast_acl_list.
335  *
336  */
337 
338 /*!
339  * \brief Context structure passed to \ref ast_curl_open_socket_default_cb
340  */
342  /*!
343  * The acl should provide a whitelist. Request to servers
344  * with addresses not allowed by the acl will be rejected.
345  */
346  const struct ast_acl_list *acl;
347  /*!
348  * Identifying info placed at the start of log and trace messages.
349  */
350  char *debug_info;
351  /*!
352  * \internal
353  * Set by the callback and passed to curl.
354  */
355  curl_socket_t sockfd;
356 };
357 
358 /*!
359  * \brief A default implementation of an open socket callback.
360 
361  * This is an implementation of the function described
362  * by CURLOPT_OPENSOCKETFUNCTION that checks the request's IP
363  * address against a user-supplied ast_acl_list and either rejects
364  * the request if the IP address isn't allowed, or opens a socket
365  * and returns it to curl.
366  * See the CURLOPT_OPENSOCKETFUNCTION documentation for more info.
367  *
368  * \param client_data A pointer to whatever structure you passed to
369  * \ref ast_curler in the \p curl_write_data parameter.
370  * \param purpose Will always be CURLSOCKTYPE_IPCXN
371  * \param address The request server's resolved IP address
372  *
373  * \return A socket opened by socket() or -1 to signal an error.
374  */
375 curl_socket_t curl_open_socket_cb(void *client_data,
376  curlsocktype purpose, struct curl_sockaddr *address);
377 
378 void curl_open_socket_data_free(void *obj);
379 
380 /*!
381  * @}
382  */
383 
384 /*!
385  * \defgroup OptionalData Optional Data
386  * \ingroup curl_wrappers
387  * @{
388 
389  * \brief Structure pased to \ref ast_curler with infrequenty used
390  * control data.
391  */
393  /*!
394  * If not set, AST_CURL_USER_AGENT
395  * (defined in asterisk.h) will be used.
396  */
397  const char *user_agent;
398  /*!
399  * Set this to limit the amount of data in each call to
400  * ast_curl_write_cb_t.
401  */
403  /*!
404  * Set this to a custom function that has a matching
405  * prototype, set it to \ref ast_curl_open_socket_default_cb
406  * to use the default callback, or leave it at NULL
407  * to not use any callback.
408  * \note Will not be called if open_socket_data is NULL.
409  */
410  curl_opensocket_callback curl_open_socket_cb;
411  /*!
412  * Set this to whatever your curl_open_socket_cb needs.
413  * If using \ref ast_curl_open_socket_default_cb, this MUST
414  * be set to an \ref ast_curl_open_socket_data structure.
415  * If set to NULL, curl_open_socket_cb will not be called.
416  */
418 };
419 
420 /*!
421  * @}
422  */
423 
424 /*!
425  * \defgroup requests Making Requests
426  * \ingroup curl_wrappers
427  * @{
428  */
429 
430 /*!
431  * \brief Perform a curl request.
432  *
433  * \param url The URL to request.
434  * \param request_timeout If > 0, timeout after this number of seconds.
435  * \param curl_write_data A pointer to a \ref curl_write_data structure. If
436  * curl_write_data.output is NULL, open_memstream will be called to
437  * provide one and the resulting data will be available in
438  * curl_write_data.stream_buffer with the number of bytes
439  * retrieved in curl_write_data.stream_bytes_downloaded.
440  * You must free curl_write_data.stream_buffer yourself with
441  * ast_std_free() when you no longer need it.
442  * \param curl_header_data A pointer to a \ref ast_curl_header_data structure.
443  * The headers read will be in the curl_header_data.headers
444  * ast_variable list which you must free with ast_variables_destroy()
445  * when you're done with them.
446  * \param curl_open_socket_data A pointer to an \ref curl_open_socket_data
447  * structure or NULL if you don't need it.
448  * \retval An HTTP response code.
449  * \retval -1 for internal error.
450  */
451 long curler(const char *url, int request_timeout,
452  struct curl_write_data *write_data,
454  struct curl_open_socket_data *open_socket_data);
455 
456 /*!
457  * \brief Really simple document retrieval to memory
458  *
459  * \param url The URL to retrieve
460  * \param returned_length Pointer to a size_t to hold document length.
461  * \param returned_data Pointer to a buffer which will be updated to
462  * point to the data. Must be freed with ast_std_free() after use.
463  * \param headers Pointer to an ast_variable * that will contain
464  * the response headers. Must be freed with ast_variables_destroy()
465  * Set to NULL if you don't need the headers.
466  * \retval An HTTP response code.
467  * \retval -1 for internal error.
468  */
469 long curl_download_to_memory(const char *url, size_t *returned_length,
470  char **returned_data, struct ast_variable **headers);
471 
472 /*!
473  * \brief Really simple document retrieval to file
474  *
475  * \param url The URL to retrieve.
476  * \param filename The filename to save it to.
477  * \retval An HTTP response code.
478  * \retval -1 for internal error.
479  */
480 long curl_download_to_file(const char *url, char *filename);
481 
482 /*!
483  * @}
484  */
485 
486 /*!
487  * @}
488  */
489 #endif /* _CURL_UTILS_H */
const char * user_agent
Definition: curl_utils.h:397
size_t curl_write_cb(char *data, size_t size, size_t nmemb, void *clientp)
A default implementation of a write data callback.
Definition: curl_utils.c:150
size_t curl_header_cb(char *data, size_t size, size_t nitems, void *client_data)
A default implementation of a header callback.
Definition: curl_utils.c:39
Context structure passed to ast_curl_open_socket_default_cb.
Definition: curl_utils.h:341
const struct ast_acl_list * acl
Definition: curl_utils.h:346
size_t max_header_len
Definition: curl_utils.h:173
Structure for variables, used for configurations and for channel variables.
Data structure used for ast_sip_push_task_wait_serializer.
char * stream_buffer
Definition: curl_utils.h:276
Wrapper for an ast_acl linked list.
Definition: acl.h:76
Context structure passed to ast_curl_write_default_cb.
Definition: curl_utils.h:245
Access Control of various sorts.
Context structure passed to ast_curl_header_default_cb.
Definition: curl_utils.h:163
long curler(const char *url, int request_timeout, struct curl_write_data *write_data, struct curl_header_data *header_data, struct curl_open_socket_data *open_socket_data)
Perform a curl request.
Definition: curl_utils.c:232
size_t max_download_bytes
Definition: curl_utils.h:250
void * curl_open_socket_data
Definition: curl_utils.h:417
curl_socket_t curl_open_socket_cb(void *client_data, curlsocktype purpose, struct curl_sockaddr *address)
A default implementation of an open socket callback.
Definition: curl_utils.c:205
size_t bytes_downloaded
Definition: curl_utils.h:267
size_t per_write_buffer_size
Definition: curl_utils.h:402
long curl_download_to_file(const char *url, char *filename)
Really simple document retrieval to file.
Definition: curl_utils.c:321
long curl_download_to_memory(const char *url, size_t *returned_length, char **returned_data, struct ast_variable **headers)
Really simple document retrieval to memory.
Definition: curl_utils.c:300
curl_opensocket_callback curl_open_socket_cb
Definition: curl_utils.h:410
char * debug_info
Definition: curl_utils.h:260
struct ast_variable * headers
Definition: curl_utils.h:182
size_t stream_bytes_downloaded
Definition: curl_utils.h:281