2019-09-30 10:53:41 -04:00
|
|
|
/******************************************************************************
|
|
|
|
|
* Copyright (c) Huawei Technologies Co., Ltd. 2018-2019. All rights reserved.
|
|
|
|
|
* iSulad licensed under the Mulan PSL v1.
|
|
|
|
|
* You can use this software according to the terms and conditions of the Mulan PSL v1.
|
|
|
|
|
* You may obtain a copy of Mulan PSL v1 at:
|
|
|
|
|
* http://license.coscl.org.cn/MulanPSL
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
|
|
|
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
|
|
|
|
|
* PURPOSE.
|
|
|
|
|
* See the Mulan PSL v1 for more details.
|
|
|
|
|
* Author: tanyifeng
|
|
|
|
|
* Create: 2018-11-08
|
|
|
|
|
* Description: provide container http function
|
|
|
|
|
******************************************************************************/
|
|
|
|
|
#include <curl/curl.h>
|
|
|
|
|
#include <curl/easy.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
|
|
#include "http.h"
|
|
|
|
|
#include "buffer.h"
|
|
|
|
|
#include "log.h"
|
|
|
|
|
#include "utils.h"
|
|
|
|
|
|
|
|
|
|
size_t fwrite_buffer(const char *ptr, size_t eltsize, size_t nmemb, void *buffer_)
|
|
|
|
|
{
|
|
|
|
|
size_t size = eltsize * nmemb;
|
|
|
|
|
struct Buffer *buffer = buffer_;
|
|
|
|
|
int status = 0;
|
|
|
|
|
|
|
|
|
|
status = buffer_append(buffer, ptr, size);
|
|
|
|
|
if (status != 0) {
|
|
|
|
|
ERROR("Failed to write buffer\n");
|
|
|
|
|
}
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t fwrite_file(const void *ptr, size_t size, size_t nmemb, void *stream)
|
|
|
|
|
{
|
|
|
|
|
size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
|
|
|
|
|
return written;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
size_t fwrite_null(char *ptr, size_t eltsize, size_t nmemb, void *strbuf)
|
|
|
|
|
{
|
|
|
|
|
return eltsize * nmemb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void free_http_get_options(struct http_get_options *options)
|
|
|
|
|
{
|
|
|
|
|
if (options == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
free(options->accepts);
|
|
|
|
|
options->accepts = NULL;
|
|
|
|
|
|
|
|
|
|
free(options->authorization);
|
|
|
|
|
options->authorization = NULL;
|
|
|
|
|
|
|
|
|
|
free(options->unix_socket_path);
|
|
|
|
|
options->unix_socket_path = NULL;
|
|
|
|
|
|
|
|
|
|
/* The options->output is a FILE pointer, we should not free it here */
|
|
|
|
|
free(options);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void http_global_init(void)
|
|
|
|
|
{
|
|
|
|
|
curl_global_init(CURL_GLOBAL_NOTHING);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void http_global_cleanup(void)
|
|
|
|
|
{
|
|
|
|
|
curl_global_cleanup();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
struct curl_slist *http_get_chunk_header(const struct http_get_options *options)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
int nret;
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
struct curl_slist *chunk = NULL;
|
|
|
|
|
char *header = NULL;
|
|
|
|
|
|
|
|
|
|
if (options->with_header_auth && options->authorization) {
|
|
|
|
|
if (strlen(options->authorization) > (SIZE_MAX - strlen("Authorization: ")) - 1) {
|
|
|
|
|
ERROR("Invalid authorization option");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
len = strlen(options->authorization) + strlen("Authorization: ") + 1;
|
|
|
|
|
header = util_common_calloc_s(len);
|
|
|
|
|
if (header == NULL) {
|
|
|
|
|
ERROR("Out of memory");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2019-12-29 15:59:28 +08:00
|
|
|
nret = snprintf(header, len, "Authorization: %s", options->authorization);
|
|
|
|
|
if (nret < 0 || (size_t)nret >= len) {
|
2019-09-30 10:53:41 -04:00
|
|
|
ERROR("Failed to print string");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
chunk = curl_slist_append(chunk, header);
|
|
|
|
|
free(header);
|
|
|
|
|
header = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->with_header_json) {
|
|
|
|
|
chunk = curl_slist_append(chunk, "Content-Type: application/json");
|
|
|
|
|
// Disable "Expect: 100-continue"
|
|
|
|
|
chunk = curl_slist_append(chunk, "Expect:");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->with_header_accept && options->accepts) {
|
|
|
|
|
if (strlen(options->accepts) > (SIZE_MAX - strlen("Accept: ")) - 1) {
|
|
|
|
|
ERROR("Invalid accepts option");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
len = strlen(options->accepts) + strlen("Accept: ") + 1;
|
|
|
|
|
header = util_common_calloc_s(len);
|
|
|
|
|
if (header == NULL) {
|
|
|
|
|
ERROR("Out of memory");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
2019-12-29 15:59:28 +08:00
|
|
|
nret = snprintf(header, len, "Accept: %s", options->accepts);
|
|
|
|
|
if (nret < 0 || (size_t)nret >= len) {
|
2019-09-30 10:53:41 -04:00
|
|
|
ERROR("Failed to print string");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
chunk = curl_slist_append(chunk, header);
|
|
|
|
|
free(header);
|
|
|
|
|
header = NULL;
|
|
|
|
|
}
|
|
|
|
|
out:
|
|
|
|
|
if (ret) {
|
|
|
|
|
curl_slist_free_all(chunk);
|
|
|
|
|
chunk = NULL;
|
|
|
|
|
}
|
|
|
|
|
free(header);
|
|
|
|
|
|
|
|
|
|
return chunk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int http_custom_options(CURL *curl_handle, const struct http_get_options *options)
|
|
|
|
|
{
|
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
|
|
if (curl_handle == NULL || options == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->unix_socket_path) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_UNIX_SOCKET_PATH, options->unix_socket_path);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->with_head) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_HEADER, 1L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->with_body == 0) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_NOBODY, 1L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* disable progress meter, set to 0L to enable and disable debug output */
|
|
|
|
|
if (options->show_progress == 0) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L);
|
|
|
|
|
} else if (options->show_progress && options->progressinfo && options->progress_info_op) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSFUNCTION, options->progress_info_op);
|
|
|
|
|
/* pass the struct pointer into the progress function */
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_PROGRESSDATA, options->progressinfo);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
|
|
|
|
|
} else {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 0L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->input) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, options->input);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, options->input_len);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_POST, 1L);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void close_file(FILE *pagefile)
|
|
|
|
|
{
|
|
|
|
|
if (pagefile != NULL) {
|
|
|
|
|
fclose(pagefile);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_rpath(char *rpath)
|
|
|
|
|
{
|
|
|
|
|
free(rpath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void check_buf_len(const char *errbuf)
|
|
|
|
|
{
|
|
|
|
|
size_t len = 0;
|
|
|
|
|
len = strlen(errbuf);
|
|
|
|
|
if (len > 0) {
|
|
|
|
|
fprintf(stderr, "%s%s", errbuf, ((errbuf[len - 1] != '\n') ? "\n" : ""));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void buffer_empty_on_condition(struct http_get_options *options)
|
|
|
|
|
{
|
|
|
|
|
if (options == NULL) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (options->output && options->outputtype == HTTP_REQUEST_STRBUF) {
|
|
|
|
|
buffer_empty(options->output);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (options->with_header_auth && options->authorization) {
|
|
|
|
|
options->with_header_auth = 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void curl_getinfo_on_condition(long *response_code, CURL *curl_handle, char **tmp)
|
|
|
|
|
{
|
|
|
|
|
if (response_code != NULL) {
|
|
|
|
|
curl_easy_getinfo(curl_handle, CURLINFO_RESPONSE_CODE, response_code);
|
|
|
|
|
}
|
|
|
|
|
curl_easy_getinfo(curl_handle, CURLINFO_REDIRECT_URL, tmp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int ensure_path_file(char **rpath, const struct http_get_options *options, FILE **pagefile)
|
|
|
|
|
{
|
|
|
|
|
if (util_ensure_path(rpath, options->output)) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
*pagefile = util_fopen(*rpath, "w+");
|
|
|
|
|
if (*pagefile == NULL) {
|
|
|
|
|
ERROR("Failed to open file %s\n", options->output);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct curl_slist *set_custom_header(CURL *curl_handle, const struct http_get_options *options)
|
|
|
|
|
{
|
|
|
|
|
struct curl_slist *chunk = NULL;
|
|
|
|
|
chunk = http_get_chunk_header(options);
|
|
|
|
|
if (chunk) {
|
|
|
|
|
/* set our custom set of headers */
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, chunk);
|
|
|
|
|
}
|
|
|
|
|
return chunk;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int http_request(const char *url, struct http_get_options *options, long *response_code, int recursive_len)
|
|
|
|
|
{
|
|
|
|
|
#define MAX_REDIRCT_NUMS 32
|
|
|
|
|
CURL *curl_handle = NULL;
|
|
|
|
|
CURLcode curl_result = CURLE_OK;
|
|
|
|
|
struct curl_slist *chunk = NULL;
|
|
|
|
|
FILE *pagefile = NULL;
|
|
|
|
|
char *rpath = NULL;
|
|
|
|
|
int ret = 0;
|
|
|
|
|
char errbuf[CURL_ERROR_SIZE] = { 0 };
|
|
|
|
|
bool strbuf_args;
|
|
|
|
|
bool file_args;
|
|
|
|
|
char *redir_url = NULL;
|
|
|
|
|
char *tmp = NULL;
|
|
|
|
|
|
|
|
|
|
if (recursive_len + 1 >= MAX_REDIRCT_NUMS) {
|
|
|
|
|
ERROR("reach the max redirect num");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
http_global_init();
|
|
|
|
|
/* init the curl session */
|
|
|
|
|
curl_handle = curl_easy_init();
|
|
|
|
|
if (curl_handle == NULL) {
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* set URL to get here */
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_URL, url);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1L);
|
|
|
|
|
/* complete connection within 5 seconds */
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_CONNECTTIMEOUT, 5L);
|
|
|
|
|
/* provide a buffer to store errors in */
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_ERRORBUFFER, errbuf);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0);
|
|
|
|
|
|
|
|
|
|
ret = http_custom_options(curl_handle, options);
|
|
|
|
|
if (ret) {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
chunk = set_custom_header(curl_handle, options);
|
|
|
|
|
|
|
|
|
|
strbuf_args = options->output && options->outputtype == HTTP_REQUEST_STRBUF;
|
|
|
|
|
file_args = options->output && options->outputtype == HTTP_REQUEST_FILE;
|
|
|
|
|
if (strbuf_args) {
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, options->output);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, fwrite_buffer);
|
|
|
|
|
} else if (file_args) {
|
|
|
|
|
/* open the file */
|
|
|
|
|
if (ensure_path_file(&rpath, options, &pagefile) == -1) {
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, pagefile);
|
|
|
|
|
curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, fwrite_file);
|
|
|
|
|
} else {
|
|
|
|
|
/* do nothing */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* get it! */
|
|
|
|
|
curl_result = curl_easy_perform(curl_handle);
|
|
|
|
|
|
|
|
|
|
if (curl_result != CURLE_OK) {
|
|
|
|
|
check_buf_len(errbuf);
|
|
|
|
|
ret = -1;
|
|
|
|
|
} else {
|
|
|
|
|
curl_getinfo_on_condition(response_code, curl_handle, &tmp);
|
|
|
|
|
if (tmp) {
|
|
|
|
|
redir_url = util_strdup_s(tmp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
close_file(pagefile);
|
|
|
|
|
free_rpath(rpath);
|
|
|
|
|
|
|
|
|
|
/* cleanup curl stuff */
|
|
|
|
|
curl_easy_cleanup(curl_handle);
|
|
|
|
|
curl_slist_free_all(chunk);
|
|
|
|
|
|
|
|
|
|
if (redir_url) {
|
|
|
|
|
buffer_empty_on_condition(options);
|
|
|
|
|
|
|
|
|
|
if (http_request(redir_url, options, response_code, recursive_len + 1)) {
|
|
|
|
|
ERROR("Failed to get http request\n");
|
|
|
|
|
ret = -1;
|
|
|
|
|
}
|
|
|
|
|
free(redir_url);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int authz_http_request(const char *username, const char *action, char **resp)
|
|
|
|
|
{
|
|
|
|
|
char *request_body = NULL;
|
|
|
|
|
char err_msg[AUTHZ_ERROR_MSG_SIZE] = { 0 };
|
|
|
|
|
long response_code = 0;
|
|
|
|
|
int ret = 0;
|
2020-01-19 00:06:40 -05:00
|
|
|
int nret = 0;
|
2019-09-30 10:53:41 -04:00
|
|
|
size_t length = 0;
|
|
|
|
|
struct http_get_options *options = NULL;
|
|
|
|
|
if (strlen(username) > ((SIZE_MAX - strlen(action)) - strlen(":")) - 1) {
|
|
|
|
|
ERROR("Invalid arguments");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
length = strlen(username) + strlen(":") + strlen(action) + 1;
|
|
|
|
|
request_body = util_common_calloc_s(length);
|
|
|
|
|
if (request_body == NULL) {
|
|
|
|
|
ERROR("Out of memory");
|
|
|
|
|
*resp = util_strdup_s("Inernal server error: Out of memory");
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
2020-01-19 00:06:40 -05:00
|
|
|
nret = snprintf(request_body, length, "%s:%s", username, action);
|
2019-12-29 15:59:28 +08:00
|
|
|
if (nret < 0 || (size_t)nret >= length) {
|
2019-09-30 10:53:41 -04:00
|
|
|
ERROR("Failed to print string");
|
|
|
|
|
free(request_body);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
options = util_common_calloc_s(sizeof(struct http_get_options));
|
|
|
|
|
if (options == NULL) {
|
|
|
|
|
ERROR("Failed to malloc http_get_options");
|
|
|
|
|
*resp = util_strdup_s("Inernal server error: Out of memory");
|
|
|
|
|
free(request_body);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
options->with_head = 1;
|
|
|
|
|
options->with_header_json = 1;
|
|
|
|
|
options->input = request_body;
|
|
|
|
|
options->input_len = strlen(request_body);
|
|
|
|
|
options->unix_socket_path = util_strdup_s(AUTHZ_UNIX_SOCK);
|
|
|
|
|
|
|
|
|
|
ret = http_request(AUTHZ_REQUEST_URL, options, &response_code, 0);
|
|
|
|
|
if (ret != 0) {
|
|
|
|
|
ERROR("Failed to request authz plugin. Is server running ?");
|
|
|
|
|
*resp = util_strdup_s("Failed to request authz plugin. Is server running ?");
|
|
|
|
|
ret = -1;
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
if (response_code != StatusOK) {
|
2020-01-19 00:06:40 -05:00
|
|
|
ret = -1;
|
|
|
|
|
nret = snprintf(err_msg, sizeof(err_msg), "action '%s' for user '%s': permission denied", action, username);
|
|
|
|
|
if (nret < 0 || (size_t)nret >= sizeof(err_msg)) {
|
2019-09-30 10:53:41 -04:00
|
|
|
ERROR("Out of memory");
|
|
|
|
|
*resp = util_strdup_s("Inernal server error: Out of memory");
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
*resp = util_strdup_s(err_msg);
|
|
|
|
|
goto out;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out:
|
|
|
|
|
free(request_body);
|
|
|
|
|
if (options != NULL) {
|
|
|
|
|
free(options->unix_socket_path);
|
|
|
|
|
free(options);
|
|
|
|
|
}
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
2019-12-25 15:50:34 +08:00
|
|
|
|