C++ Libcurl在较大内容的readfunction回调中导致写访问冲突

Boa*_*rdy 5 c++ smtp libcurl

我正在开发一个C++项目,我使用libcurl通过SMTP发送电子邮件.代码几乎适用于小内容,但是,对于较大的电子邮件,它会引发写入访问冲突,我看不出任何原因.

以下是我使用curl函数发送邮件的方式:

curl = curl_easy_init();
        //curl_easy_setopt(curl, CURLOPT_FORBID_REUSE, 1);
        if (curl)
        {
            if (this->useVerboseOutput)
            {
                curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
            }
            curl_easy_setopt(curl, CURLOPT_URL, smtpAddress.c_str());


            if (this->useTLS)
            {
                curl_easy_setopt(curl, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL);
            }
            if (this->useAuthentication)
            {
                if (this->username.empty() || this->password.empty())
                {
                    throw logic_error("SMTP username or password has not been set but authentication is enabled");
                }
                curl_easy_setopt(curl, CURLOPT_USERNAME, this->username.c_str());
                curl_easy_setopt(curl, CURLOPT_PASSWORD, this->password.c_str());
            }

            curl_easy_setopt(curl, CURLOPT_MAIL_FROM, this->fromAddress.c_str());
            curl_easy_setopt(curl, CURLOPT_MAIL_RCPT, recipients);
            curl_easy_setopt(curl, CURLOPT_READDATA, this);
            curl_easy_setopt(curl, CURLOPT_READFUNCTION, &EmailSender::invoke_write_data);
            curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);

            //Send the message
            res = curl_easy_perform(curl);
Run Code Online (Sandbox Code Playgroud)

下面是read函数回调

size_t EmailSender::invoke_write_data(void *data, size_t size, size_t nmemb, void* pInstance)
{
    return ((EmailSender*)pInstance)->payload_source(data, size, nmemb);
}

size_t EmailSender::payload_source(void *ptr, size_t size, size_t nmemb)
{
    //struct upload_status *upload_ctx = (struct upload_status*)userp;
    const char *data;

    if ((size == 0) || (nmemb == 0) || ((size*nmemb) < 1)) {
        return 0;
    }

    if (this->upload_ctx.lines_read < this->lineArray.size())
    {
        data = this->lineArray.at(this->upload_ctx.lines_read).c_str();
    }
    else
    {
        return 0;
    }

    if (data) {
        size_t len = strlen(data);
        memcpy(ptr, data, len);
        this->upload_ctx.lines_read++;

        return len;
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

this->upload_ctx.lines_read++;在第5次调用后崩溃(向量lineArray和upload_ctx-> lines_read中有6行是5.

完整的错误消息是:

Exception thrown at 0x00007FFF4E8F16D7 (vcruntime140d.dll) in myapp.exe: 0xC0000005: Access violation writing location 0x00000205CC8AC000.
Run Code Online (Sandbox Code Playgroud)

YSC*_*YSC 3

根据以下文件CURLOPT_READFUNCTION

概要

#include <curl/curl.h>
size_t read_callback(char *buffer, size_t size, size_t nitems, void *instream);
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_READFUNCTION, read_callback);
Run Code Online (Sandbox Code Playgroud)

描述

将指针传递给回调函数,如上面的原型所示。

当 libcurl 需要读取数据以便将其发送到对等方时,该回调函数就会被调用 - 就像您要求它上传或发布数据到服务器一样。指针缓冲区指向的数据区域最多应填充函数字节数的乘积 sizenitems

你写了:

size_t len = strlen(data);
memcpy(ptr, data, len);
Run Code Online (Sandbox Code Playgroud)

由于len仅取决于要发送的数据,并且由于您不检查它是否小于size*nitemsnmemb对您而言),因此您可能会从 libcurl 分配的缓冲区中写出,因此调用未定义的行为。

由于您按行工作,而 libcurl 按字节工作,因此您需要重新编写应用程序以跟踪部分写入的行,或者完全放弃行的概念。