无垠之码

深度剖析代码之道


libcurl-指北针

libcurl是一个易用免费的http客户端库,其支持众多应用层传输协议,如DICT(dict://dict.org/d:computer,RFC2229定义的基于TCP的字典查询协议)、FILE、FTP, FTPS、GOPHER(诞生于1980年代末期的早期互联网协议,用于浏览和检索文本文档,已淘汰)、GOPHERS、HTTP、HTTPS、IMAP、IMAPS、LDAP、LDAPS、MQTT、POP3、POP3S、RTMP、RTMPS、RTSP、SCP、SFTP、SMB、SMBS、SMTP、SMTPS、TELNET、TFTP、WS和WSS等协议。并且在功能特性上libcurl支持SSL证书,以及POST|PUT|FTP等上传,各种代理隧道模式,身份认证方式,文件传输中断恢复等。libcurl支持各种操作系统平台,如常见的Linux,Windows,FreeBSD, OpenBSD,甚至不常见的UnixWare, HURD, Windows, Amiga, OS/2等

接口类型


easy接口

libcurl提供简单高效同步的easy接口簇供开发者快速上手,目前大量开源软件依赖这些接口。按照libcurl的官方文档,该使用模式只需要调用curl_easy_init获取curl句柄(后续调用easy函数簇入参),再设置相关的curl句柄的options(其中必须设置CURLOPT_URL,该参数决定后续模式和对象),最后调用curl_easy_perform执行操作,如果没有后续操作,或者并不需要长连接,记得调用curl_easy_cleanup销毁句柄。


typedef CURL* (*curl_easy_init_func)();
typedef CURLcode (*curl_easy_setopt_func)(CURL*, CURLoption, ...);
typedef CURLcode (*curl_easy_perform_func)(CURL*);
typedef void (*curl_easy_cleanup_func)(CURL*);
typedef const char* (*curl_easy_strerror_func)(CURLcode errornum);
typedef struct curl_slist* (*curl_slist_append_func)(struct curl_slist* list, const char* string);
typedef void (*curl_slist_free_all_func)(struct curl_slist* list);

typedef struct {
  curl_easy_init_func init;
  curl_easy_setopt_func setopt;
  curl_easy_perform_func perform;
  curl_easy_cleanup_func cleanup;
  curl_easy_strerror_func strerror;
  curl_slist_append_func slist_append;
  curl_slist_free_all_func slist_free_all;
} CurlInterface;

int onvif_auth_lack_verify(CurlInterface *ci, int argc, const char **argv) {
  int res = -1;
  CURL *curl = NULL;

  if (argc < 1) {
    log_error("onvif_auth_lack poc verify require url");
    goto done;
  }

  const char *url = argv[0];
  onvif_auth_resp_body_t chunk = {0};

  curl = ci->init();
  if (!curl) {
    log_error("onvif_auth_lack failed to initialize curl");
    goto done;
  }

  CHECK_F2_LABEL((ci->setopt(curl, CURLOPT_URL, url) == CURLE_OK), done, "set url fail");
  CHECK_F2_LABEL((ci->setopt(curl, CURLOPT_TIMEOUT, 5L)) == CURLE_OK, done, "set timeout failed");
  CHECK_F2_LABEL((ci->setopt(curl, CURLOPT_WRITEFUNCTION, onvif_resp_callback) == CURLE_OK), done, "set write func fail");
  CHECK_F2_LABEL((ci->setopt(curl, CURLOPT_WRITEDATA, (void *)&chunk)) == CURLE_OK, done, "set write data fail");

  CURLcode curl_res = ci->perform(curl);
  if (curl_res != CURLE_OK) {
    log_error("CURL error: %s", ci->strerror(curl_res));
    goto done;
  }

  if (memcmp(chunk.data + 2048 - 10, ONVIF_AUTH_LACK_MAGIC_STRING, 10) == 0) {
    log_trace("Vulnerability found at URL: %s", url);
  }

  res = 0;  // success
done:
  if (curl) ci->cleanup(curl);
  return res;
}

multi接口

The multi interface is the asynchronous brother in the family and it also offers multiple transfers using a single thread and more. Get a grip of how to work with it in the multi interface overview.

The multi interface offers several abilities that the easy interface does not. They are mainly:

  1. Enable a “pull” interface. The application that uses libcurl decides where and when to ask libcurl to get/send data.

  2. Enable multiple simultaneous transfers in the same thread without making it complicated for the application.

  3. Enable the application to wait for action on its own file descriptors and curl’s file descriptors simultaneously.

  4. Enable event-based handling and scaling transfers up to and beyond thousands of parallel connections.


multi = curl_multi_init();
easy1 = curl_easy_init();  // 例如下载A
easy2 = curl_easy_init();  // 下载B
curl_multi_add_handle(multi, easy1);
curl_multi_add_handle(multi, easy2);

int running;
do {
    curl_multi_poll(multi, NULL, 0, 1000, NULL);
    curl_multi_perform(multi, &running);

    CURLMsg *msg;
    int msgs_left;
    while ((msg = curl_multi_info_read(multi, &msgs_left))) {
        if (msg->msg == CURLMSG_DONE) {
            CURL *e = msg->easy_handle;
            CURLcode res = msg->data.result;
            printf("Transfer completed with result: %s\n", curl_easy_strerror(res));
            curl_multi_remove_handle(multi, e);
            curl_easy_cleanup(e);
        }
    }
} while (running > 0);

curl_multi_cleanup(multi);

3.参考文献

  1. https://curl.se/libcurl/c/example.html
comments powered by Disqus