Asterisk - The Open Source Telephony Project GIT-master-1f1c5bb
curl_utils.h
Go to the documentation of this file.
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
36While libcurl is extremely flexible in what it allows you to do,
37that flexibility comes at complexity price. The convenience wrappers
38defined here aim to take away some of that complexity for run-of-the-mill
39requests.
40
41\par A Basic Example
42
43If 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
58If 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
79If you need more control, you can specify callbacks to capture
80the response headers, do something other than write the data
81to a memory buffer, or do some special socket manipulation like
82check that the server's IP address matched an acl.
83
84Let's write the data to a file, capture the headers,
85and make sure the server's IP address is whitelisted.
86
87The default callbacks can do that so all we need to do is
88supply 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
117If you need even more control, you can supply your own
118callbacks as well. This is a silly example of providing
119your own write callback. It's basically what
120ast_curler_write_to_file() does.
121
122\code
123static 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
130static 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 */
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 */
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 */
215size_t curl_header_cb(char *data, size_t size,
216 size_t nitems, void *client_data);
217
218void 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 */
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 */
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 */
311size_t curl_write_cb(char *data, size_t size, size_t nmemb, void *clientp);
312
313void 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 */
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 */
375curl_socket_t curl_open_socket_cb(void *client_data,
376 curlsocktype purpose, struct curl_sockaddr *address);
377
378void 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 */
451long 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 */
469long 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 */
480long curl_download_to_file(const char *url, char *filename);
481
482/*!
483 * @}
484 */
485
486/*!
487 * @}
488 */
489#endif /* _CURL_UTILS_H */
Access Control of various sorts.
char * address
Definition: f2c.h:59
void curl_write_data_free(void *obj)
Definition: curl_utils.c:134
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
void curl_header_data_free(void *obj)
Definition: curl_utils.c:26
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
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
void curl_open_socket_data_free(void *obj)
Definition: curl_utils.c:193
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
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
static char url[512]
Wrapper for an ast_acl linked list.
Definition: acl.h:76
Structure for variables, used for configurations and for channel variables.
Context structure passed to ast_curl_header_default_cb.
Definition: curl_utils.h:163
struct ast_variable * headers
Definition: curl_utils.h:182
size_t max_header_len
Definition: curl_utils.h:173
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
curl_socket_t sockfd
Definition: curl_utils.h:355
size_t per_write_buffer_size
Definition: curl_utils.h:402
curl_opensocket_callback curl_open_socket_cb
Definition: curl_utils.h:410
void * curl_open_socket_data
Definition: curl_utils.h:417
const char * user_agent
Definition: curl_utils.h:397
Context structure passed to ast_curl_write_default_cb.
Definition: curl_utils.h:245
size_t bytes_downloaded
Definition: curl_utils.h:267
char * stream_buffer
Definition: curl_utils.h:276
size_t max_download_bytes
Definition: curl_utils.h:250
int _internal_memstream
Definition: curl_utils.h:286
char * debug_info
Definition: curl_utils.h:260
size_t stream_bytes_downloaded
Definition: curl_utils.h:281
Data structure used for ast_sip_push_task_wait_serializer