4

I have compiled static libcurl for android but continuously receiving the CurlRes code 6 i.e. CURLE_COULDNT_RESOLVE_HOST.

Android.mk

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := Curl

LOCAL_SRC_FILES := prebuild/libcurl.a

include $(PREBUILT_STATIC_LIBRARY)

include $(CLEAR_VARS)

LOCAL_MODULE    := ccSharedLib

LOCAL_SRC_FILES := main-jni.cpp

LOCAL_STATIC_LIBRARIES := Curl 

include $(BUILD_SHARED_LIBRARY)

main-jni.cpp

extern "C" {
    size_t write_data(void *ptr, size_t size, size_t count, FILE *stream)
        {
            size_t written;
            written = fwrite(ptr, size, count, stream);
            printf("data sent, size = %lu",written);
            return written;
        }

    jint
        Java_com_example_testlibcurl_MainActivity_test1( JNIEnv*  env,
                                        jobject  thiz, jstring downloadDirectoryPath)
        {
            CURLcode res;
            res = curl_global_init(CURL_GLOBAL_ALL);

            jint temp = 3;

            printf("Method called");
            const char *nativeDownloadDirPath = env->GetStringUTFChars(downloadDirectoryPath,0);
            // Test code for calling methods of libCURL
            CURL *curl;
            FILE *fp;
            std::string s = "http://travel.paintedstork.com/blog/wp-content/uploads/2012/10/2013-calendar-images-1.jpg";
            curl = curl_easy_init();
            if(curl)
            {
                fp = fopen(nativeDownloadDirPath, "wb");
            res = curl_easy_setopt(curl, CURLOPT_URL, s.c_str());
                res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
                res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
                res = curl_easy_perform(curl);
                curl_easy_cleanup(curl);
                if(fp)
                    fclose(fp);
            }
            return res;
        }
}

This code is downloading the image from a web source, but every time when "curl_easy_perform" method is called it gives the error code 6. I have checked this with different URL but still unsuccessful :( ...

"android.permission.INTERNET" and "android.permission.WRITE_EXTERNAL_STORAGE" permissions already given in Manifest file.

Any pointer to solve this will be a great help.

TMZ
  • 110
  • 1
  • 9
  • 1
    Working on a multi device (cross platform) application which call the web services from C/C++ code. The same code is working fine with iOS and windows application but fails in Android due to this issue. – TMZ Mar 07 '13 at 06:30

2 Answers2

1

Make sure that you pass a valid file name and writable directory path to fopen.

I was getting

Fatal signal 11 (SIGSEGV) at 0x00000010 (code=1) ...

because fopen was trying to open a directory instead of a file.

Check the logs in LogCat from Eclipse or using adb logcat for other possible errors.

I've tested your code with the following modifications and it works.

MainActivity.java:

public class MainActivity
{
    private static final String TAG = MainActivity.class.getName();

    static
    {
        try
        {
            System.loadLibrary("mynativelib");
        }
        catch (UnsatisfiedLinkError ex)
        {
            Log.e(TAG, "WARNING: Could not load native library: " + ex.getMessage());
        }
    }

    public static native int DownloadFile(String downloadDirectoryPath);

   @Override
   protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        int res = DownloadFile(Environment.getExternalStorageDirectory().getAbsolutePath()
                + File.separator + Environment.DIRECTORY_DOWNLOADS + File.separator 
                + "test.jpg");
        Log.d(TAG, "Result Code: " + res);
    }
}

main-jni.cpp

#include <jni.h>
#include <android/log.h>
#include <string>
#include <curl/curl.h>

#define LOG_TAG "native"

#define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#define LOG_WARN(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOG_DEBUG(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

#ifdef __cplusplus
extern "C"
{
#endif

    // [FIX for Android 4.2.x]
    // "WARNING: Could not load native library:
    // Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol "__exidx_end" referenced by"
    // http://stackoverflow.com/a/14501998/313113
    void __exidx_start()
    {
    }
    void __exidx_end()
    {
    }

    size_t write_data(void *ptr, size_t size, size_t count, FILE *stream)
    {
        size_t written;
        written = fwrite(ptr, size, count, stream);
        LOG_DEBUG("Writing data to file stream %u", written);
        return written;
    }

    jint Java_com_company_awesomeapp_MainActivity_DownloadFile(JNIEnv* env, jobject thiz,
            jstring downloadDirectoryPath)
    {
        CURLcode res;
        res = curl_global_init(CURL_GLOBAL_ALL);

        jint temp = 3;

        LOG_DEBUG("Downloading file");
        const char *nativeDownloadDirPath = env->GetStringUTFChars(downloadDirectoryPath, 0);

        LOG_DEBUG(nativeDownloadDirPath);

        CURL *curl;
        FILE *fp;
        std::string url = "http://travel.paintedstork.com/blog/wp-content/uploads/2012/10/2013-calendar-images-1.jpg";

        curl = curl_easy_init();
        if (curl)
        {
            fp = fopen(nativeDownloadDirPath, "wb");
            res = curl_easy_setopt(curl, CURLOPT_URL, url.c_str());

            LOG_DEBUG("Before write function");
            res = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data);
            LOG_DEBUG("After write function");

            LOG_DEBUG("Before write data");
            res = curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp);
            LOG_DEBUG("After write data");

            res = curl_easy_perform(curl);
            curl_easy_cleanup(curl);

            if (fp) fclose(fp);
        }

        env->ReleaseStringUTFChars(downloadDirectoryPath, nativeDownloadDirPath);

        return res;
    }

/**
     * The VM calls JNI_OnLoad when the native library is loaded (for example, through System.loadLibrary).
     * JNI_OnLoad must return the JNI version needed by the native library.
     *
     * @see http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#JNI_OnLoad
     */JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
    {
        JNIEnv* env;
        if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
        {
            return -1;
        }

        // [*] Get jclass with env->FindClass.
        // [*] Register methods with env->RegisterNatives.
        //jniRegisterNativeMethods(env, "dev/android/sample/AndroidNDKSampleActivity", sMethods, NELEM(sMethods));

        return JNI_VERSION_1_6;
    }

/**
     * The VM calls JNI_OnUnload when the class loader containing the native library is garbage collected.
     * This function can be used to perform cleanup operations.
     *
     * Because this function is called in an unknown context (such as from a finalizer),
     * the programmer should be conservative on using Java VM services, and refrain from arbitrary
     * Java call-backs.
     * @see http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#JNI_OnUnload
     */JNIEXPORT void JNI_OnUnload(JavaVM *vm, void *reserved)
    {

    }


#ifdef __cplusplus
}
#endif

The file will be saved at: /storage/emulated/0/Download/test.jpg

Alex Bitek
  • 6,529
  • 5
  • 47
  • 77
  • Thanks a lot for quick response. I have updated the code as per your suggestion but still having the same issue. The name which I was passing from the android application is a valid file name but the variable name was confusing(downloadDirectoryPath). As you have tested the code and its working fine on your side so this means there is no issue with the code right now. Do you think there may be some issue in **libcurl** static library compilation? Or any other tweak which I am missing on my side? – TMZ Mar 08 '13 at 04:54
  • 1
    @TMZ What curl version are you using? Does it have http://c-ares.haxx.se support? http://curl.haxx.se/dev/readme-ares.html Using **objdump -a libcurl.a** my libcurl has the following objects: http://paste.kde.org/690056/ Are you sure you have an internet connection on your mobile device on which you are testing the code? I tested the code on a phone with Android 4.2.2 on which there was **no** network connection and I get "CURLE_COULDNT_RESOLVE_HOST" too. If you're using the emulator there may be issues with that environment as well... Have you compiled libcurl for armeabi or armeabi-v7a? – Alex Bitek Mar 08 '13 at 07:10
1

This can also happen if you compile libcurl without threaded resolver (CMake option ENABLE_THREADED_RESOLVER) while targeting Android API level less than 23.

The problem is because implementation of CMake/CurlTests.c contains this line:

#ifndef gethostbyaddr_r
  (void)gethostbyaddr_r;
#endif

However, if you check the netdb.h header in Android NDK, you will see

#if __ANDROID_API__ >= 23
int gethostbyaddr_r(const void* __addr, socklen_t __length, int __type, struct hostent* __ret, char* __buf, size_t __buf_size, struct hostent** __result, int* __h_errno_ptr) __INTRODUCED_IN(23);
#endif /* __ANDROID_API__ >= 23 */

This means that this function is not available when targeting Android older than Marshmallow. This is problematic, as testing for HAVE_GETHOSTBYNAME_R succeeds, while testing for HAVE_GETHOSTBYNAME_R_6 fails (but should succeed) with following error (observable in CMakeError.log):

/path/to/curl/CMake/CurlTests.c:128:9: error: use of undeclared identifier 'gethostbyaddr_r'
  (void)gethostbyaddr_r;
        ^
1 error generated.

This then results with no-op behaviour in function Curl_ipv4_resolve_r in file hostip4.c, as it expects that after HAVE_GETHOSTBYNAME_R is defined, that at least one of HAVE_GETHOSTBYNAME_R_5, HAVE_GETHOSTBYNAME_R_6 or HAVE_GETHOSTBYNAME_R_3. Therefore, curl doesn't even try to resolve the host.

To fix this problem, simply remove those problematic lines from CMake/CurlTest.c.

I didn't try building curl with configure script, as in official documentation, but even if you do, it clearly states that you should target Android M or above if you plan on supporting SSL via OpenSSL, but AFAIK for simple HTTP support it should work.

The problem is gone if you use threaded resolver, as it uses different system API for performing the DNS resolution (getaddrinfo in function Curl_getaddrinfo_ex in curl_addrinfo.c). getaddrinfo is more modern API, but may not be available on all platform curl targets, such as embedded systems, which may also lack thread support. Therefore, curl uses the traditional approach in DNS resolution when threads are disabled.

P.S. Sorry for resurrecting this question after more than 7 years, but after facing the exact same problem as you, this StackOverflow question was the only similar Google search result

DoDo
  • 2,248
  • 2
  • 22
  • 34