V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
razrlele
V2EX  ›  问与答

c++ libcurl 发送 https post 请求相关问题

  •  
  •   razrlele · 2019-10-10 14:51:15 +08:00 · 3425 次点击
    这是一个创建于 1876 天前的主题,其中的信息可能已经有所发展或是发生改变。

    发送请求代码是

    size_t WriteBack( void *ptr, size_t size, size_t nmemb, void *stream ) {
        size_t realsize = size * nmemb;
        string *buffer = (string*)stream;
        buffer->append((const char *)ptr, realsize);
    
        return realsize;
    }
    
    bool ProfileOperator::CurlPost(const string &url, const string &postdata, string &res_buffer)
    {
      CURL *curl;
      CURLcode code;
      curl = curl_easy_init();
      if (curl){
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteBack);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&res_buffer);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        LOG(DEBUG) << curl_version();
        LOG(DEBUG) << "ready to send req, url: " << url << " postdata: " << postdata;
        code = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        if( code != CURLE_OK){
          LOG(WARNING) << "post req failed, res_buffer:" << res_buffer << " err:" << curl_easy_strerror(code);
          return false;
        }
      } else {
        return false;
      }
      return true;
    }
    

    然后在执行到 curl_easy_cleanup(curl) 函数的时候会栈溢出,gdb 信息如下

    Program terminated with signal 11, Segmentation fault.
    #0  0x000000330f523e44 in X509_certificate_type () from /usr/lib64/libcrypto.so.10
    Missing separate debuginfos, use: debuginfo-install addops-zookeeper-client-3.4.5-1.el6.x86_64 boost153-system-1.53.0-8.el6.x86_64 boost153-thread-1.53.0-8.el6.x86_64 glibc-2.12-1.132.el6_5.2.x86_64 keyutils-libs-1.4-5.el6.x86_64 krb5-libs-1.10.3-65.el6.x86_64 libcom_err-1.41.12-24.el6.x86_64 libqlog-2.1.14-1.el6.x86
    _64 libselinux-2.0.94-7.el6.x86_64 nspr-4.8.8-3.el6.x86_64 nss-3.12.10-16.el6.x86_64 nss-softokn-freebl-3.14.3-10.el6_5.x86_64 nss-util-3.12.10-2.el6.x86_64 openldap-2.4.23-20.el6.x86_64 openssl-1.0.1e-57.el6.x86_64 snappy-1.0.5-1.el6.x86_64 zlib-1.2.3-29.el6.x86_64
    (gdb) bt
    #0  0x000000330f523e44 in X509_certificate_type () from /usr/lib64/libcrypto.so.10
    #1  0x0000003310822c3b in ssl3_check_cert_and_algorithm () from /usr/lib64/libssl.so.10
    #2  0x0000003310827951 in ssl3_connect () from /usr/lib64/libssl.so.10
    #3  0x0000003310830e07 in ssl23_connect () from /usr/lib64/libssl.so.10
    

    如果我注释掉的两行 SSL 相关配置,程序就不会栈溢出,但是发送请求的时候会报错Problem with the SSL CA cert (path? access rights?)

    size_t WriteBack( void *ptr, size_t size, size_t nmemb, void *stream ) {
        size_t realsize = size * nmemb;
        string *buffer = (string*)stream;
        buffer->append((const char *)ptr, realsize);
    
        return realsize;
    }
    
    bool ProfileOperator::CurlPost(const string &url, const string &postdata, string &res_buffer)
    {
      CURL *curl;
      CURLcode code;
      curl = curl_easy_init();
      if (curl){
        curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
        curl_easy_setopt(curl, CURLOPT_POST, 1L);
        //curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
        //curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
        curl_easy_setopt(curl, CURLOPT_POSTFIELDS, postdata.c_str());
        curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteBack);
        curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void*)&res_buffer);
        curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
        LOG(DEBUG) << curl_version();
        LOG(DEBUG) << "ready to send req, url: " << url << " postdata: " << postdata;
        code = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
        if( code != CURLE_OK){
          LOG(WARNING) << "post req failed, res_buffer:" << res_buffer << " err:" << curl_easy_strerror(code);
          return false;
        }
      } else {
        return false;
      }
      return true;
    }
    

    我在 shell 里面直接执行的 curl 命令给 https 网站发送请求没问题,所以本机上的证书应该也是没有问题的,默认用的是

    /etc/pki/tls/certs/ca-bundle.cr
    

    shell 里面的 curl 版本是

    curl 7.37.0 (x86_64-unknown-linux-gnu) libcurl/7.25.0 OpenSSL/1.0.0 zlib/1.2.3 c-ares/1.7.0 libidn/1.18 libssh2/1.4.2
    Protocols: file ftp ftps http https ldap ldaps scp sftp telnet tftp
    Features: AsynchDNS IDN Largefile NTLM NTLM_WB SSL libz TLS-SRP Metalink
    

    libcurl 版本是

    libcurl/7.65.1 OpenSSL/1.0.1e-fips zlib/1.2.3
    

    版本虽然不一致但是 libcurl 版本和 openssl 版本都要高一点,应该兼容性更好,求问是哪里还需要配置一下吗?

    4 条回复    2019-10-11 21:24:07 +08:00
    Hallelu
        1
    Hallelu  
       2019-10-10 16:31:07 +08:00   ❤️ 1
    libcurl 崩溃的问题之前遇到过,也是搞的头疼
    libcurl 本身是线程安全的,但 openssl 不是。
    设置一下禁止 libcurl 访问超市抛出的超时信号 curl_setopt(curl, CURLOPT_NOSIGNAL,1L);
    还有 init 的时候,最好在主线程调用全局初始化 curl_global_init(CURL_GLOBAL_ALL);
    在为 openssl 创建两个回调函数,楼主可以试试。
    razrlele
        2
    razrlele  
    OP
       2019-10-11 15:19:09 +08:00
    @Hallelu 尝试了 curl_easy_setopt(curl, CURLOPT_NOSIGNAL,1L)和 curl_global_init(CURL_GLOBAL_ALL),还是不可以,为 openssl 创建两个回调函数是要自己去实现证书验证吗?
    Hallelu
        3
    Hallelu  
       2019-10-11 20:11:32 +08:00
    @razrlele 不是证书验证,两个回调函数是加锁和释放锁
    razrlele
        4
    razrlele  
    OP
       2019-10-11 21:24:07 +08:00
    @Hallelu 可是我就算只起了一个线程还是崩溃。。。问题好像并不在线程不安全。。。

    囧,已经放弃走 https 了,发现请求的那个接口走 http 也可以。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3526 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 11:07 · PVG 19:07 · LAX 03:07 · JFK 06:07
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.