使用/发送POST数据时出现libcurl C问题(不是C++)


libcurl C issue when using/sending POST data (not C++)

好的,所以我一直在尝试重现这个PHP脚本的功能:

<?php
function api_query($method, array $req = array()) {
    // API settings
    $key = ''; // your API-key
    $secret = ''; // your Secret-key
    $req['method'] = $method;
    $mt = explode(' ', microtime());
    $req['nonce'] = $mt[1];
    // generate the POST data string
    $post_data = http_build_query($req, '', '&');
    $sign = hash_hmac("sha512", $post_data, $secret);
    // generate the extra headers
    $headers = array(
            'Sign: '.$sign,
            'Key: '.$key,
    );
    // our curl handle (initialize if required)
    static $ch = null;
    if (is_null($ch)) {
            $ch = curl_init();
            curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; Cryptsy API PHP client; '.php_uname('s').'; PHP/'.phpversion().')');
    }
    curl_setopt($ch, CURLOPT_URL, 'https://www.cryptsy.com/api');
    curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
    // run the query
    $res = curl_exec($ch);
    if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch));
    $dec = json_decode($res, true);
    if (!$dec) throw new Exception('Invalid data received, please make sure connection is working and requested API exists');
    return $dec;
}

我已经(至少我相信我已经)负责创建http_build_query调用,这是C所没有的,只是通过自己构建查询。通过使用gettimeofday(),我在mtStr中有$mt变量,并且我正在使用openSSL中的HMAC函数用我的密钥对post_data进行签名。

我发现C中的CURLOPT_HTTPHEADER选项需要一个curl_slist链表,而不是像PHP中那样只需要一个数组,但我似乎遇到了问题的地方是Post Data。请求将通过,但返回的错误为"检查POST数据"。因此,我返回到libcurl文档,发现CURLOPT_POSTFIELDS需要对要发布的数据使用void*。所以我创建了void*pData=post_data(其中post_data只是一个char数组,而不是指针,所以我在这里看不到问题),但它仍然表示它不能有效地识别我的身份,请检查post数据。所以,如果你能看看这个,告诉我哪里出了问题,我将不胜感激。。。

注意,我没有显示我的API和密钥,因为我不能自己更改它们,我宁愿不把它们给出来,但我知道这会让你很难在没有密码帐户的情况下通过编译自己来检查我的东西,但我现在想保密,但有一些伪密钥在那里,所以有一些东西可以哈希,长度合适等等

char *handle_url( char *url )
{
CURL *curl;
struct url_data data;
data.size = 0;
data.data = malloc( 4096 );
if ( NULL == data.data )
{
    fprintf( stderr, "failed to allocate memory.'n" );
    return NULL;
}
data.data[0] = ''0';
CURLcode res;
// API settings, API key and secret key for your account
char key[] = "9498fbb723961a42816a10bc559cgda7ded2ed8e";
char secret[] = "687cd29def08a7861446c3b4b9c97996c8472e7dd8922da147d3b1343e52e99125d24ace90729fbb";
// method to use against the API, will make changable later
char *method = "mytrades";
// This is required to replace the microtime()/explode methods to
// generate the nonce value required to use the cryptsy API
struct timeval time;
gettimeofday( &time, NULL );
long mt = ( (unsigned long long)time.tv_sec * 1000000 ) + time.tv_usec;
// C is much morer strict than PHP about types so we create a buffer for the
// string representation of the nonce
char mtStr[ 128 ];
sprintf( mtStr, "%lu", mt );
// C does not have a build_http_query as PHP does so
// we just create the string neccessary with strcpy
// and strcat
char post_data[ strlen("method=") + strlen(method) + strlen("&nonce=") + strlen(mtStr) + 1];
strcpy( post_data, "method=" );
strcat( post_data, method );
strcat( post_data, "&" );
strcat( post_data, "nonce=" );
strcat( post_data, mtStr );
printf( "%s", post_data);
printf( "'n");
void *pData = post_data;
//sha512 needs 128 characters
unsigned char *result;
unsigned int len = 128;
result = (unsigned char *)malloc( sizeof(char) * len );
HMAC_CTX ctx;
HMAC_CTX_init( &ctx );
// using sha512
HMAC_Init_ex( &ctx, secret, strlen(secret), EVP_sha512(), NULL );
HMAC_Update( &ctx, (unsigned char *)&post_data, strlen(post_data) );
HMAC_Final( &ctx, result, &len );
HMAC_CTX_cleanup( &ctx );
/*
printf( "'nHMAC digest: " );
for ( int i = 0; i != len; i++ )
{
    printf( "%02x", (unsigned int)result[i] );
} */
printf("'n");
// This is the start of setting up the $headers array as in the PHP script
const char *header1 = "Sign: ";
size_t PrefixL = strlen(header1);
char resBuffer[PrefixL + 2 * len + 1];
strcpy( resBuffer, header1);
char *p = &resBuffer[PrefixL];
for ( unsigned int i = 0; i < len; i++ )
{
    sprintf( p, "%02x", (unsigned int)result[i]);
    p += 2;
}
printf( "%s'n", resBuffer );
const char *header2 = "Key: ";
size_t PrefixR = strlen(header2);
char keyBuffer[PrefixR + 2 * strlen(key) + 1];
strcpy( keyBuffer, header2);
strcat( keyBuffer, key);
printf( "'n%s'n", keyBuffer );

// So now resBuffer is Sign: <128 character sha512 hash here>
// and keyBuffer is Key: <API key here>
free(result);
//struct curl_httppost *chttppostlist = NULL; //a possibility about sending chunked post data not currently used
// originally I set this up so that Sign and Key would be in the array in the same
// manner as the PHP script but libcurl in C needs a curl_slist 
const char *headerArray[2];
headerArray[0] = resBuffer;
headerArray[1] = keyBuffer;
// So then we just create a curl_slist and add the resBuffer and keyBuffer (headerArray
// [0] and headerArray[1] respectively) to the curl_slist.
struct curl_slist *cslist= NULL;
curl_slist_append(cslist, headerArray[0]);
curl_slist_append(cslist, headerArray[1]);
curl = curl_easy_init();
if ( curl )
{
    curl_easy_setopt( curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible Cryptsy API C Client)");
    curl_easy_setopt( curl, CURLOPT_URL, url );
    curl_easy_setopt( curl, CURLOPT_POST, 1 ); // is this necessary? isnt in PHP
    curl_easy_setopt( curl, CURLOPT_POSTFIELDS, pData ); // <--- something wrong here?
    curl_easy_setopt( curl, CURLOPT_HTTPHEADER, cslist );
    curl_easy_setopt( curl, CURLOPT_WRITEFUNCTION, write_data );
    curl_easy_setopt( curl, CURLOPT_WRITEDATA, &data );
    curl_easy_setopt( curl, CURLOPT_SSL_VERIFYPEER, 0 );
    res = curl_easy_perform( curl );
    if ( res != CURLE_OK )
    {
        fprintf( stderr, "curl_easy_perform() failed: %s'n", curl_easy_strerror(res));
    }
    curl_slist_free_all( cslist );
    curl_easy_cleanup( curl );
}
return data.data;
}
#include <iostream>
#include <cstdlib>
#include <cstring>
#include "json_parsers.h"
extern "C" {
#include <curl/curl.h>
#include <sys/time.h>
#include <limits.h>
#include <openssl/hmac.h>
#include <openssl/sha.h>
}
#define OK  0
#define NO_INPUT 1
#define TOO_LONG 2
using namespace std;
struct url_data {
size_t size;
char *data;
};
size_t write_data( void *ptr, size_t size, size_t nmemb, struct url_data *data )
{
    size_t index = data->size;
    size_t n = ( size * nmemb );
    char *tmp;
    data->size += ( size * nmemb );
#ifdef DEBUG
    fprintf( stderr, "data at %p size=%ld nmemb=%ld'n", ptr,
        size, nmemb );
#endif
tmp = (char *)realloc( data->data, data->size + 1); // + 1 for 'n
if ( tmp )
{
    data->data = tmp;
}
else
{
    if ( data->data )
    {
        free( data->data );
    }
    fprintf( stderr, "Failed to allocate memory.'n" );
    return 0;
}
memcpy( ( data->data + index ), ptr, n );
data->data[ data->size ] = ''0';
return size * nmemb;
}

char*cryptsy_api(char*url,char*method_in,char*marketid,int limit,char*ordertype,char*quantity,char*price,char*order id){

CURL *curl;
struct url_data data;
data.size = 0;
data.data = (char *)malloc(4096);
if (data.data == NULL)
{
    cerr << "Failed to allocate memory.'n" << endl;
    return NULL;
}
data.data[0] = ''0';
CURLcode res;
char strInt[5];
if (limit != NULL)
{
    sprintf(strInt, "%d", limit);
}
// API settings, API key and secret key for your account
char key[] = "9498fbb723961a42816a10bc559cfda7ded2ed8f";
char secret[] =  "687cd29def08a7861446c3b4b9c97996c8472e7dd8922da147d3b1346e52e99125d24ace90729fb3";
char *method = method_in;
// This is required to replace the microtime()/explode methods to
// generate the nonce value required to use the cryptsy API
struct timeval time;
gettimeofday(&time, NULL);
long mt = (unsigned long long)time.tv_sec;
char mtStr[10];
sprintf(mtStr, "%ul", mt);

// C does not have a build_http_query as PHP does so
// we just create the string neccessary with strcpy
// and strcat
char post_data[ strlen("method=") + strlen(method) + strlen("&nonce=") + strlen(mtStr) + strlen("&marketid=110") + strlen("&limit=1000") + strlen("&ordertype=sell&quantity=1000000&price=0.00000000")+ 1];
strcpy(post_data, "method=");
strcat(post_data, method);
strcat(post_data, "&nonce=");
strcat(post_data, mtStr);
if (marketid != NULL)
{
    strcat(post_data, "&marketid=");
    strcat(post_data, marketid);
}
if (limit != NULL)
{
    strcat(post_data, "&limit=");
    strcat(post_data, strInt);
}
if (ordertype != NULL)
{
    strcat(post_data, "&ordertype=");
    strcat(post_data, ordertype);
}
if (quantity != NULL)
{
    strcat(post_data, "&quantity=");
    strcat(post_data, quantity);
}
if (price != NULL)
{
    strcat(post_data, "&price=");
    strcat(post_data, price);
}
if (orderid != NULL)
{
    strcat(post_data, "&orderid=");
    strcat(post_data, orderid);
}
unsigned char *result;
unsigned int len = SHA512_DIGEST_LENGTH;
result = (unsigned char *)malloc(sizeof(char) * len);
HMAC_CTX ctx;
HMAC_CTX_init( &ctx );
// using SHA512
HMAC_Init_ex( &ctx, secret, strlen(secret), EVP_sha512(), NULL);
HMAC_Update( &ctx, (unsigned char *)&post_data, strlen(post_data));
HMAC_Final( &ctx, result, &len);
HMAC_CTX_cleanup( &ctx );
const char *header1 = "Sign: ";
size_t PrefixL = strlen(header1);
char resBuffer[PrefixL + 2 * len + 1];
strcpy(resBuffer, header1);
char *p = &resBuffer[PrefixL];
for (unsigned int i = 0; i < len; i++)
{
    sprintf(p, "%02x", (unsigned int)result[i]);
    p += 2;
}
const char *header2 = "Key: ";
size_t PrefixR = strlen(header2);
char keyBuffer[PrefixR + 2 * strlen(key) + 1];
strcpy(keyBuffer, header2);
strcat(keyBuffer, key);
free(result);
struct curl_slist *cslist = NULL;
cslist = curl_slist_append(cslist, resBuffer);
cslist = curl_slist_append(cslist, keyBuffer);
curl = curl_easy_init();
if (curl)
{
    curl_easy_setopt(curl, CURLOPT_USERAGENT, "Mozilla/4.0 (compatible Cryptsy API C Client)");
    curl_easy_setopt(curl, CURLOPT_URL, url);
    curl_easy_setopt(curl, CURLOPT_POST, 1L);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDS, post_data);
    curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, strlen(post_data));
    curl_easy_setopt(curl, CURLOPT_HTTPHEADER, cslist);
    curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
    curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
    curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
    res = curl_easy_perform(curl);
    if (res != CURLE_OK)
    {
        cerr << "curl_easy_perform() failed " << curl_easy_strerror(res) << "'n" <<   endl;
    }
    curl_slist_free_all(cslist);
    curl_easy_cleanup(curl);
}
return data.data;
}

很抱歉无法格式化最后一位,但你明白了这个想法,是的,我更改了我的API键:)

相关文章: