/*
SSSD
libcurl tevent integration
Copyright (C) Red Hat, 2016
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <talloc.h>
#include <tevent.h>
#include "util/tev_curl.h"
/* This limit in the same one as KCM_REPLY_MAX */
static bool global_is_curl_initialized;
/**
* @brief The main structure of the tcurl module.
*
* Use tcurl_init() to initialize it, then pass to the request.
* Should be kept opaque in the future.
*
* @see tcurl_init()
*/
struct tcurl_ctx {
/* See where we set CURLMOPT_TIMERFUNCTION */
/* Since we want the API to be non-blocking, all the transfers use
* the curl's multi interface:
* and then each transfer also uses an easy interface instance for
* the transfer's private data
*/
};
/**
* @brief A tevent wrapper around curl socket
*/
struct tcurl_sock {
};
int response_code);
{
switch (crv) {
/* HTTP error does not fail the whole request, just returns the error
* separately
*/
case CURLE_OK:
return EOK;
case CURLE_URL_MALFORMAT:
return EBADMSG;
case CURLE_COULDNT_CONNECT:
return EHOSTUNREACH;
return EACCES;
case CURLE_OUT_OF_MEMORY:
return ENOMEM;
case CURLE_OPERATION_TIMEDOUT:
return ETIMEDOUT;
case CURLE_SSL_ISSUER_ERROR:
case CURLE_SSL_CACERT_BADFILE:
case CURLE_SSL_CACERT:
case CURLE_SSL_CERTPROBLEM:
return ERR_INVALID_CERT;
case CURLE_SSL_CRL_BADFILE:
case CURLE_USE_SSL_FAILED:
case CURLE_SSL_CIPHER:
case CURLE_SSL_CONNECT_ERROR:
return ERR_SSL_FAILURE;
return ERR_UNABLE_TO_VERIFY_PEER;
return ERR_UNABLE_TO_RESOLVE_HOST;
default:
break;
}
return EIO;
}
{
switch (crv) {
case CURLM_OK:
return EOK;
case CURLM_BAD_SOCKET:
return EPIPE;
case CURLM_OUT_OF_MEMORY:
return ENOMEM;
case CURLM_BAD_HANDLE:
case CURLM_BAD_EASY_HANDLE:
case CURLM_UNKNOWN_OPTION:
return EINVAL;
case CURLM_INTERNAL_ERROR:
return ERR_INTERNAL;
default:
break;
}
return EIO;
}
{
if (global_is_curl_initialized == false) {
"Cannot initialize global curl options [%d]\n", ret);
return EIO;
}
}
global_is_curl_initialized = true;
return EOK;
}
{
int flags = 0;
switch (curlflags) {
case CURL_POLL_IN:
flags |= TEVENT_FD_READ;
break;
case CURL_POLL_OUT:
flags |= TEVENT_FD_WRITE;
break;
case CURL_POLL_INOUT:
break;
}
return flags;
}
{
long response_code = 0;
char *done_url;
if (easy_handle == NULL) {
return;
}
if (DEBUG_IS_SET(SSSDBG_TRACE_FUNC)) {
/* not fatal since we need this only for debugging */
} else {
}
}
goto done;
}
goto done;
}
/* If there was no fatal error, let's read the response code
* and mark the request as done */
goto done;
}
done:
}
{
int pending;
case CURLMSG_DONE:
break;
default:
break;
}
}
}
void *data)
{
int curl_flags = 0;
int running_handles;
return;
}
if (flags & TEVENT_FD_READ) {
}
if (flags & TEVENT_FD_WRITE) {
}
/* multi_socket_action might invalidate tcs when the transfer ends,
* so we need to store tctx separately
*/
}
/**
* @brief Registers a curl's socket with tevent
*
* Creates a private structure, registers the socket with tevent and finally
* registers the tcurl_sock structure as a private pointer for the curl
* socket for later
*/
int flags)
{
return NULL;
}
return NULL;
}
return tcs;
}
/* libcurl informs the application about socket activity to wait for with
* this callback */
int action,
void *userp,
void *socketp)
{
int flags = 0;
return 1;
}
"Activity on curl socket %d socket data %p\n", s, socketp);
switch (action) {
case CURL_POLL_IN:
case CURL_POLL_OUT:
case CURL_POLL_INOUT:
/* There is some activity on a socket */
/* If this socket doesn't have private data, it must be a new one,
* let's start tracking it with tevent
*/
return 1;
}
} else {
/* If we are already tracking this socket, just set the correct
* flags for tevent and pass the control to tevent
*/
"BUG: No private data for socket %d\n", s);
return 1;
}
}
break;
case CURL_POLL_REMOVE:
/* This socket is being closed by curl, so we need to.. */
"BUG: Trying to remove an untracked socket %d\n", s);
}
/* ..stop tracking the socket with the multi handle.. */
/* ..and stop tracking the fd with tevent */
break;
default:
return 1;
}
return 0;
}
{
int running_handles;
0,
"Still tracking %d outstanding requests\n", running_handles);
}
struct tevent_timer *te,
struct timeval current_time,
void *private_data)
{
}
long timeout_ms,
void *userp)
{
if (timeout_ms == -1) {
/* man curlmopt_timerfunction(3) says:
* A timeout_ms value of -1 means you should delete your timer.
*/
return 0;
}
/* There is only one timer per multi handle, so it makes sense to cancel
* the previous one.
*
* There is only one timeout for the application to handle for the
* entire multi handle, no matter how many individual easy handles
* that have been added or transfers that are in progress. The timer
* callback will be updated with the current nearest-in-time period to
* wait.
*/
return -1;
}
return 0;
}
{
return 0;
}
return 0;
}
struct tevent_context *ev)
{
/* Per the manpage it is safe to call the initialization multiple
* times, as long as this is done before any other curl calls to
* make sure we don't mangle the global curl environment
*/
ret = tcurl_global_init();
goto fail;
}
goto fail;
}
goto fail;
}
"Cannot set CURLMOPT_SOCKETDATA [%d]: %s\n",
goto fail;
}
/*
* When there is some activity on a socket associated with the multi
* handle, then the handle_socket() function will be called with the
* global context as private data
*/
"Cannot set CURLMOPT_SOCKETFUNCTION [%d]: %s\n",
goto fail;
}
/* When integrated in a mainloop, the curl multi interface must
* kick off the communication in another eventloop tick. Similar
* to the handle_socet function, the tcurl context is passed in
* as private data
*/
"Cannot set CURLMOPT_TIMERFUNCTION [%d]: %s\n",
goto fail;
}
"Cannot set CURLMOPT_TIMERDATA [%d]: %s\n",
}
return tctx;
fail:
return NULL;
}
({ \
\
if (__curl_code == CURLE_OK) { \
} else { \
} \
__ret; \
})
void *userdata)
{
/* zero signifies an EOF */
return 0;
}
return realsize;
}
void *userdata)
{
return CURL_READFUNC_ABORT;
}
return CURL_READFUNC_ABORT;
}
return readbytes;
}
struct tcurl_request {
const char *url;
const char *socket;
/* Associated tcurl context if this request is in progress. */
};
struct tcurl_request_state {
int response_code;
};
struct tevent_req *
struct tevent_context *ev,
struct tcurl_request *tcurl_req,
long int timeout)
{
return NULL;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
}
goto done;
}
done:
}
return req;
}
int response_code)
{
/* To handle case where we fail to obtain request from private data. */
return;
}
/* This request is no longer associated with tcurl context. */
if (process_error != EOK) {
return;
}
return;
}
struct tevent_req *req,
int *_response_code)
{
}
if (_response_code != NULL) {
}
return EOK;
}
static struct curl_slist *
{
}
return NULL;
}
return new;
}
static errno_t
struct curl_slist **_slist)
{
int i;
return EOK;
}
return ENOMEM;
}
}
/* Add a dummy header to suppress libcurl adding Expect 100-continue which
* was causing libcurl to always wait for the internal timeout when sending
*/
return ENOMEM;
}
return EOK;
}
static int
{
}
}
}
return 0;
}
static struct tcurl_request *
const char *socket_path,
const char *url,
const char **headers,
{
return NULL;
}
goto done;
}
/* Setup a curl easy handle. This handle contains state for the request
* and is later associated with curl multi handle which performs
* asynchronous processing. */
goto done;
}
goto done;
}
if (socket_path != NULL) {
goto done;
}
}
goto done;
}
goto done;
}
if (socket_path != NULL) {
goto done;
}
}
/* Curl will tell the underlying protocol about incoming data length.
* In case of HTTP it will add a sane Content-Length header. */
goto done;
}
}
done:
return NULL;
}
return tcurl_req;
}
enum tcurl_http_method method,
const char *socket_path,
const char *url,
const char **headers,
{
return NULL;
}
/* Set HTTP specific options. */
goto done;
}
switch (method) {
case TCURL_HTTP_GET:
/* Nothing to do here. GET is default. */
break;
case TCURL_HTTP_PUT:
goto done;
}
break;
case TCURL_HTTP_POST:
goto done;
}
break;
case TCURL_HTTP_DELETE:
goto done;
}
break;
}
done:
return NULL;
}
return tcurl_req;
}
struct tevent_context *ev,
enum tcurl_http_method method,
const char *socket_path,
const char *url,
const char **headers,
int timeout)
{
return NULL;
}
}
return req;
}
struct tevent_req *req,
int *_http_code,
{
}
{
}
const char *capath,
const char *cacert,
bool verify_peer,
bool verify_host)
{
return ret;
}
return ret;
}
return ret;
}
}
return ret;
}
}
return EOK;
}
const char *cert,
const char *key)
{
return EINVAL;
}
return ret;
}
/* If client's private key is in separate file. */
return ret;
}
}
return EOK;
}
const char *username,
const char *password)
{
return ret;
}
return ret;
}
return ret;
}
return EOK;
}