c++ – Error C2440 in mod_http_cache\aws.c When compiling FreeSwitch from Source in VS 2017 – Stack Overflow


I’m trying to compile FreeSwitch from source in VS2017 for Win32 (as they advised in the Documentation). Everything compiled properly except I get the following error:

Error C2440 ‘=’: cannot convert from ‘switch_curl_slist_t *(__stdcall
*)(http_profile_t *,switch_curl_slist_t *,const char *,unsigned int,const char *,const char *,const unsigned int,char **)’ to
‘switch_curl_slist_t *(__cdecl *)(http_profile *,switch_curl_slist_t
*,const char *,unsigned int,const char *,const char *,const unsigned int,char
**)’ mod_http_cache c:\fs_git\src\mod\applications\mod_http_cache\aws.c line
336

I’ve tried to set the C++ Language Standard to 14 and also 17 but still have the same error! I’ve attached my VS2017 project property here too.

My mod_http_cache Project Properties:

I’ve also tried to clean the solution and rebuild again, but still facing the same error.

Would appreciate the help from anyone who can advise what is the problem and how to resolve it?

Thank you so much.

Herewith below is the source code:

/*
 * aws.c for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 * Copyright (C) 2013-2014, Grasshopper
 *
 * Version: MPL 1.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is aws.c for FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
 *
 * The Initial Developer of the Original Code is Grasshopper
 * Portions created by the Initial Developer are Copyright (C)
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 * Chris Rienzo <[email protected]>
 * Quoc-Bao Nguyen <[email protected]>
 *
 * aws.c -- Some Amazon Web Services helper functions
 *
 */
#include "aws.h"
#include <switch.h>

#if defined(HAVE_OPENSSL)
#include <openssl/hmac.h>
#include <openssl/sha.h>
#endif

#if defined(HAVE_OPENSSL)
/**
 * Calculate HMAC-SHA256 hash of a message
 * @param buffer buffer to store the HMAC-SHA256 version of message as byte array
 * @param buffer_length length of buffer
 * @param key buffer that store the key to run HMAC-SHA256
 * @param key_length length of the key
 * @param message message that will be hashed
 * @return byte array, equals to buffer
 */
static char *hmac256(char* buffer, unsigned int buffer_length, const char* key, unsigned int key_length, const char* message)
{
    if (!key || zstr(message) || buffer_length < SHA256_DIGEST_LENGTH) {
        return NULL;
    }

    HMAC(EVP_sha256(),
         key,
         (int)key_length,
         (unsigned char *)message,
         strlen(message),
         (unsigned char*)buffer,
         &buffer_length);

    return (char*)buffer;
}


/**
 * Calculate HMAC-SHA256 hash of a message
 * @param buffer buffer to store the HMAC-SHA256 version of the message as hex string
 * @param key buffer that store the key to run HMAC-SHA256
 * @param key_length length of the key
 * @param message message that will be hashed
 * @return hex string that store the HMAC-SHA256 version of the message
 */
static char *hmac256_hex(char* buffer, const char* key, unsigned int key_length, const char* message)
{
    char hmac256_raw[SHA256_DIGEST_LENGTH] = { 0 };

    if (hmac256(hmac256_raw, SHA256_DIGEST_LENGTH, key, key_length, message) == NULL) {
        return NULL;
    }

    for (unsigned int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        snprintf(buffer + i*2, 3, "%02x", (unsigned char)hmac256_raw[i]);
    }
    buffer[SHA256_DIGEST_LENGTH * 2] = '\0';

    return buffer;
}


/**
 * Calculate SHA256 hash of a message
 * @param buffer buffer to store the SHA256 version of the message as hex string
 * @param string string to be hashed
 * @return hex string that store the SHA256 version of the message
 */
static char *sha256_hex(char* buffer, const char* string)
{
    unsigned char sha256_raw[SHA256_DIGEST_LENGTH] = { 0 };

    SHA256((unsigned char*)string, strlen(string), sha256_raw);

    for (unsigned int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        snprintf(buffer + i*2, 3, "%02x", sha256_raw[i]);
    }
    buffer[SHA256_DIGEST_LENGTH * 2] = '\0';

    return buffer;
}


/**
 * Get current time_stamp. Example: 20190724T110316Z
 * @param format format of the time in strftime format
 * @param buffer buffer to store the result
 * @param buffer_length length of buffer
 * @return current time stamp
 */
static char *get_time(char* format, char* buffer, unsigned int buffer_length)
{
    switch_time_exp_t time;
    switch_size_t size;

    switch_time_exp_gmt(&time, switch_time_now());

    switch_strftime(buffer, &size, buffer_length, format, &time);

    return buffer;
}


/**
 * Get signature key
 * @param key_signing buffer to store signature key
 * @param aws_s3_profile AWS profile
 * @return key_signing
 */
static char* aws_s3_signature_key(char* key_signing, switch_aws_s3_profile* aws_s3_profile) {

    char key_date[SHA256_DIGEST_LENGTH];
    char key_region[SHA256_DIGEST_LENGTH];
    char key_service[SHA256_DIGEST_LENGTH];
    char* aws4_secret_access_key = switch_mprintf("AWS4%s", aws_s3_profile->access_key_secret);

    if (!hmac256(key_date, SHA256_DIGEST_LENGTH, aws4_secret_access_key, (unsigned int)strlen(aws4_secret_access_key), aws_s3_profile->date_stamp)
        || !hmac256(key_region, SHA256_DIGEST_LENGTH, key_date, SHA256_DIGEST_LENGTH, aws_s3_profile->region)
        || !hmac256(key_service, SHA256_DIGEST_LENGTH, key_region, SHA256_DIGEST_LENGTH, "s3")
        || !hmac256(key_signing, SHA256_DIGEST_LENGTH, key_service, SHA256_DIGEST_LENGTH, "aws4_request")) {
        key_signing = NULL;
    }

    switch_safe_free(aws4_secret_access_key);

    return key_signing;
}

/**
 * Get query string that will be put together with the signature
 * @param aws_s3_profile AWS profile
 * @return the query string (must be freed)
 */
static char* aws_s3_standardized_query_string(switch_aws_s3_profile* aws_s3_profile)
{
    char* credential;
    char expires[10];
    char* standardized_query_string;

    credential = switch_mprintf("%s%%2F%s%%2F%s%%2Fs3%%2Faws4_request", aws_s3_profile->access_key_id, aws_s3_profile->date_stamp, aws_s3_profile->region);
    switch_snprintf(expires, 9, "%" SWITCH_TIME_T_FMT, aws_s3_profile->expires);

    standardized_query_string = switch_mprintf(
            "X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=%s&X-Amz-Date=%s&X-Amz-Expires=%s&X-Amz-SignedHeaders=host",
            credential, aws_s3_profile->time_stamp, expires
    );

    switch_safe_free(credential);

    return standardized_query_string;
}

/**
 * Get request string that is used to build string to sign
 * @param aws_s3_profile AWS profile
 * @return the request string (must be freed)
 */
static char* aws_s3_standardized_request(switch_aws_s3_profile* aws_s3_profile) {

    char* standardized_query_string = aws_s3_standardized_query_string(aws_s3_profile);

    char* standardized_request = switch_mprintf(
        "%s\n/%s\n%s\nhost:%s.%s\n\nhost\nUNSIGNED-PAYLOAD",
        aws_s3_profile->verb, aws_s3_profile->object, standardized_query_string, aws_s3_profile->bucket, aws_s3_profile->base_domain
    );

    switch_safe_free(standardized_query_string);

    return standardized_request;
}


/**
 * Create the string to sign for a AWS signature version 4
 * @param standardized_request request string that is used to build string to sign
 * @param aws_s3_profile AWS profile
 * @return the string to sign (must be freed)
 */
static char *aws_s3_string_to_sign(char* standardized_request, switch_aws_s3_profile* aws_s3_profile) {

    char standardized_request_hex[SHA256_DIGEST_LENGTH * 2 + 1] = {'\0'};
    char* string_to_sign;

    sha256_hex(standardized_request_hex, standardized_request);

    string_to_sign = switch_mprintf(
        "AWS4-HMAC-SHA256\n%s\n%s/%s/s3/aws4_request\n%s",
        aws_s3_profile->time_stamp, aws_s3_profile->date_stamp, aws_s3_profile->region, standardized_request_hex
    );

    return string_to_sign;
}

/**
 * Create a full query string that contains signature version 4 for AWS request
 * @param aws_s3_profile AWS profile
 * @return full query string that include the signature
 */
static char *aws_s3_authentication_create(switch_aws_s3_profile* aws_s3_profile) {
    char signature[SHA256_DIGEST_LENGTH * 2 + 1];
    char *string_to_sign;

    char* standardized_query_string;
    char* standardized_request;
    char signature_key[SHA256_DIGEST_LENGTH];
    char* query_param;

    // Get standardized_query_string
    standardized_query_string = aws_s3_standardized_query_string(aws_s3_profile);

    // Get standardized_request
    standardized_request = aws_s3_standardized_request(aws_s3_profile);

    // Get string_to_sign
    string_to_sign = aws_s3_string_to_sign(standardized_request, aws_s3_profile);

    // Get signature_key
    if (!aws_s3_signature_key(signature_key, aws_s3_profile)
        // Get signature
        || !hmac256_hex(signature, signature_key, SHA256_DIGEST_LENGTH, string_to_sign)) {
        query_param = NULL;
    } else {
        // Build final query string
        query_param = switch_mprintf("%s&X-Amz-Signature=%s", standardized_query_string, signature);
    }

    switch_safe_free(string_to_sign);
    switch_safe_free(standardized_query_string);
    switch_safe_free(standardized_request);

    return query_param;
}
#endif

/**
 * Append Amazon S3 query params to request if necessary
 * @param headers to add to. AWS signature v4 requires no header to be appended
 * @param profile with S3 credentials
 * @param content_type of object (PUT only)
 * @param verb http methods (GET/PUT)
 * @param url full url
 * @param block_num block number, only used by Azure
 * @param query_string pointer to query param string that will be calculated
 * @return updated headers
 */
SWITCH_MOD_DECLARE(switch_curl_slist_t *) aws_s3_append_headers(
        http_profile_t *profile,
        switch_curl_slist_t *headers,
        const char *verb,
        unsigned int content_length,
        const char *content_type,
        const char *url,
        const unsigned int block_num,
        char **query_string
) {
#if defined(HAVE_OPENSSL)
    switch_aws_s3_profile aws_s3_profile;
    char* url_dup;

    if (!query_string) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Missing required arg query_string.\n");
        return headers;
    }

    // Get bucket and object name from url
    switch_strdup(url_dup, url);
    parse_url(url_dup, profile->base_domain, "s3", &aws_s3_profile.bucket, &aws_s3_profile.object);
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "bucket: %s\n", aws_s3_profile.bucket);
    switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "object: %s\n", aws_s3_profile.object);

    // Get date and time
    get_time("%Y%m%d", aws_s3_profile.date_stamp, DATE_STAMP_LENGTH);
    get_time("%Y%m%dT%H%M%SZ", aws_s3_profile.time_stamp, TIME_STAMP_LENGTH);

    // Get access key id and secret
    aws_s3_profile.access_key_id = profile->aws_s3_access_key_id;
    aws_s3_profile.access_key_secret = profile->secret_access_key;

    // Get base domain
    aws_s3_profile.base_domain = profile->base_domain;
    aws_s3_profile.region = profile->region;
    aws_s3_profile.verb = verb;
    aws_s3_profile.expires = profile->expires;

    *query_string = aws_s3_authentication_create(&aws_s3_profile);

    switch_safe_free(url_dup);
#endif
    return headers;
}

/**
 * Get key id, secret and region from env variables or config file
 * @param xml object that store config file
 * @param profile pointer that config will be written to
 * @return status
 */
SWITCH_MOD_DECLARE(switch_status_t) aws_s3_config_profile(switch_xml_t xml, http_profile_t *profile)
{
#if defined(HAVE_OPENSSL)
    switch_xml_t base_domain_xml = switch_xml_child(xml, "base-domain");
    switch_xml_t region_xml = switch_xml_child(xml, "region");
    switch_xml_t expires_xml = switch_xml_child(xml, "expires");

    // Function pointer to be called to append query params to original url
    profile->append_headers_ptr = aws_s3_append_headers;

    /* check if environment variables set the keys */
    profile->aws_s3_access_key_id = getenv("AWS_ACCESS_KEY_ID");
    profile->secret_access_key = getenv("AWS_SECRET_ACCESS_KEY");
    if (!zstr(profile->aws_s3_access_key_id) && !zstr(profile->secret_access_key)) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Using AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY environment variables for AWS S3 access for profile \"%s\"\n", profile->name);
        profile->aws_s3_access_key_id = strdup(profile->aws_s3_access_key_id);
        profile->secret_access_key = strdup(profile->secret_access_key);
    } else {
        /* use configuration for keys */
        switch_xml_t id = switch_xml_child(xml, "access-key-id");
        switch_xml_t secret = switch_xml_child(xml, "secret-access-key");
        if (!id || !secret)
        {
            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing access-key-id or secret-access-key in http_cache.conf.xml for profile \"%s\"\n", profile->name);
            return SWITCH_STATUS_FALSE;
        }

        profile->aws_s3_access_key_id = switch_strip_whitespace(switch_xml_txt(id));
        profile->secret_access_key = switch_strip_whitespace(switch_xml_txt(secret));
        if (zstr(profile->aws_s3_access_key_id) || zstr(profile->secret_access_key)) {
            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Empty access-key-id or secret-access-key in http_cache.conf.xml for profile \"%s\"\n", profile->name);
            switch_safe_free(profile->aws_s3_access_key_id);
            switch_safe_free(profile->secret_access_key);
            return SWITCH_STATUS_FALSE;
        }
    }

    // Get region
    if (!region_xml) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Missing region in http_cache.conf.xml for profile \"%s\"\n", profile->name);
        return SWITCH_STATUS_FALSE;
    }
    profile->region = switch_strip_whitespace(switch_xml_txt(region_xml));
    if (zstr(profile->region)) {
        switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Empty region in http_cache.conf.xml for profile \"%s\"\n", profile->name);
        switch_safe_free(profile->region);
        return SWITCH_STATUS_FALSE;
    }

    // Get base domain for AWS S3 compatible services. Default base domain is s3.amazonaws.com
    if (base_domain_xml) {
        profile->base_domain = switch_strip_whitespace(switch_xml_txt(base_domain_xml));
        if (zstr(profile->base_domain)) {
            switch_safe_free(profile->base_domain);
            profile->base_domain = switch_mprintf(DEFAULT_BASE_DOMAIN, profile->region);
        }
    } else
    {
        profile->base_domain = switch_mprintf(DEFAULT_BASE_DOMAIN, profile->region);
    }

    // Get expire time for URL signature
    if (expires_xml) {
        char* expires = switch_strip_whitespace(switch_xml_txt(expires_xml));
        if (!zstr(expires) && switch_is_number(expires))
        {
            profile->expires = switch_safe_atoi(expires, DEFAULT_EXPIRATION_TIME);
        } else
        {
            switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "Invalid \"expires\" in http_cache.conf.xml for profile \"%s\"\n", profile->name);
            profile->expires = DEFAULT_EXPIRATION_TIME;
        }
        switch_safe_free(expires);
    } else
    {
        profile->expires = DEFAULT_EXPIRATION_TIME;
    }

#endif

    return SWITCH_STATUS_SUCCESS;
}

/* For Emacs:
 * Local Variables:
 * mode:c
 * indent-tabs-mode:t
 * tab-width:4
 * c-basic-offset:4
 * End:
 * For VIM:
 * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet
 */

Leave a Reply

Your email address will not be published. Required fields are marked *