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

  1. #1
    Registered User
    Join Date
    Dec 2013
    Posts
    3

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

    ok so I have been trying to reproduce the functionality of this PHP script, this may seeem like a long question but if read what I wrote I only need to somehow fix the post_data as the server says that Auth Failed, please check POST Data. Original PHP script (my C code below):

    Code:
    <?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;
    }
    I have (at least I believe I have) taken care of creating the http_build_query call that C does not have just by constructing the query myself. I have the $mt variable in mtStr by using gettimeofday() and I am using the HMAC function from openSSL to sign the post_data with my secret key.
    I figured out that the CURLOPT_HTTPHEADER option in C needs a curl_slist linked lists instead of just an array like in PHP, but where I seem to be having problems is in the Post Data. The request will go through but there is an error return saying "check your POST Data". So i went back to the libcurl documentation and found that CURLOPT_POSTFIELDS requires a void* to the data you want to post. So i create void *pData = post_data (where post_data is just a char array, not a pointer so I dont see a probem here) but it still says that it can not valid my identify please check POST data. So if you could take a look at this and tell me where I am going wrong it would be appreciated...
    Note that I am not showing my API and secret keys, since I cant change them on my own I would rather not give them out but I know this makes it hard to check my stuff by compiling yourself without a cryptsy account but I'd like to keep it secret for now but there are some dummy keys in there so that there is something to hash that is the proper length etc.

    Code:
    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;
    }

  2. #2
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    You should probably redact key/secret in your C source as well.

    Your PHP code appears to keep the second part of microtime, which according to the docs is the seconds value. Your mt ... doesn't.

  3. #3
    Registered User
    Join Date
    Dec 2013
    Posts
    3
    Quote Originally Posted by tabstop View Post
    You should probably redact key/secret in your C source as well.

    Your PHP code appears to keep the second part of microtime, which according to the docs is the seconds value. Your mt ... doesn't.
    As I said in the post, those keys are bogus, they are of the correct length so that you can compile and hash something of the right size/length but not my personal API/secret key, im not that dumb man.

    I have tried to just keep the time.tv_usec portion but that is also a different length than the explode( ' ', microtime()) in PHP... but i dont think the length of the nonce actually matters, just that it is a different and higher value than the last request....

    EDIT: if i take off the *1000000 + time.tv_usec then the nonces from PHP and my code match... but it still wont validate...
    Last edited by Robert Lorenzen; 12-02-2013 at 05:55 PM.

  4. #4
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Apologies for missing that blurb in your post.

    I had just run tests to show that the tv.sec and the PHP matched, and it looks like you did too.

    The CURLOPT_POST appears to get set automatically when you use POSTFIELDS, so you can keep it or leave it at your discretion.

    I don't see you putting a \0 character at the end of p. EDIT: No wait, the sprintf will keep p null-terminated. I think that's fine.

    EDIT EDIT: According to http://curl.haxx.se/libcurl/c/curl_slist_append.html you should be keeping the return from curl_slist_append.
    Last edited by tabstop; 12-02-2013 at 06:46 PM.

  5. #5
    Registered User
    Join Date
    Dec 2013
    Posts
    3
    Quote Originally Posted by tabstop View Post
    Apologies for missing that blurb in your post.

    I had just run tests to show that the tv.sec and the PHP matched, and it looks like you did too.

    The CURLOPT_POST appears to get set automatically when you use POSTFIELDS, so you can keep it or leave it at your discretion.

    I don't see you putting a \0 character at the end of p. EDIT: No wait, the sprintf will keep p null-terminated. I think that's fine.

    EDIT EDIT: According to libcurl - curl_slist_append() you should be keeping the return from curl_slist_append.
    Thank you! Even though I read the page on curl_slist_append() i didnt see that I needed to keep the output of that function, I was treating it like strcpy or strcat. Anyhow, thanks for your help, now that I got that figured out I got to add JSON decoding.. but thanks greatly.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. sending data through usb
    By ak47 in forum C Programming
    Replies: 4
    Last Post: 03-14-2013, 10:51 PM
  2. Sending data over the network
    By codewriter in forum Tech Board
    Replies: 10
    Last Post: 05-09-2012, 06:44 AM
  3. libcURL POST items
    By Pete in forum Networking/Device Communication
    Replies: 2
    Last Post: 05-30-2007, 09:24 AM
  4. Sending an http POST request
    By jaxen in forum C++ Programming
    Replies: 5
    Last Post: 11-24-2006, 12:35 PM
  5. sending HTTP POST data with Socket
    By Overtaker in forum Networking/Device Communication
    Replies: 10
    Last Post: 09-07-2006, 10:11 AM

Tags for this Thread