"Fossies" - the Fresh Open Source Software Archive

Member "filezilla-3.48.1/src/engine/http/httpcontrolsocket.cpp" (15 May 2020, 10188 Bytes) of package /linux/misc/FileZilla_3.48.1_src.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "httpcontrolsocket.cpp" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.48.0_vs_3.48.1.

    1 #include <filezilla.h>
    2 
    3 #include "connect.h"
    4 #include "controlsocket.h"
    5 #include "engineprivate.h"
    6 #include "filetransfer.h"
    7 #include "httpcontrolsocket.h"
    8 #include "internalconnect.h"
    9 #include "request.h"
   10 
   11 #include <libfilezilla/file.hpp>
   12 #include <libfilezilla/iputils.hpp>
   13 #include <libfilezilla/local_filesys.hpp>
   14 #include <libfilezilla/tls_layer.hpp>
   15 #include <libfilezilla/uri.hpp>
   16 
   17 #include <assert.h>
   18 #include <string.h>
   19 
   20 int simple_body::data_request(unsigned char* data, unsigned int & len)
   21 {
   22     len = static_cast<unsigned int>(std::min(static_cast<size_t>(len), body_.size() - written_));
   23     memcpy(data, body_.c_str() + written_, len);
   24     written_ += len;
   25     return FZ_REPLY_CONTINUE;
   26 }
   27 
   28 
   29 file_body::file_body(fz::file & file, uint64_t start, uint64_t size, fz::logger_interface & logger)
   30     : file_(file)
   31     , start_(start)
   32     , size_(size)
   33     , logger_(logger)
   34 {
   35 }
   36 
   37 int file_body::data_request(unsigned char* data, unsigned int & len)
   38 {
   39     assert(size_ >= written_);
   40     assert(len > 0);
   41     len = static_cast<unsigned int>(std::min(static_cast<uint64_t>(len), size_ - written_));
   42     if (!len) {
   43         return FZ_REPLY_CONTINUE;
   44     }
   45     auto bytes_read = file_.read(data, len);
   46     if (bytes_read < 0) {
   47         len = 0;
   48         logger_.log(logmsg::error, _("Reading from local file failed"));
   49         return FZ_REPLY_ERROR;
   50     }
   51     else if (bytes_read == 0) {
   52         len = 0;
   53         return FZ_REPLY_ERROR;
   54     }
   55 
   56     if (progress_callback_) {
   57         progress_callback_(bytes_read);
   58     }
   59 
   60     len = static_cast<unsigned int>(bytes_read);
   61     written_ += len;
   62     return FZ_REPLY_CONTINUE;
   63 }
   64 
   65 int file_body::rewind()
   66 {
   67     if (progress_callback_) {
   68         progress_callback_(-static_cast<int64_t>(written_));
   69     }
   70     written_ = 0;
   71 
   72     int64_t s = static_cast<int64_t>(start_);
   73     if (file_.seek(s, fz::file::begin) != s) {
   74         if (!start_) {
   75             logger_.log(logmsg::error, _("Could not seek to the beginning of the file"));
   76         }
   77         else {
   78             logger_.log(logmsg::error, _("Could not seek to offset %d within file"), start_);
   79         }
   80         return FZ_REPLY_ERROR;
   81     }
   82 
   83     return FZ_REPLY_CONTINUE;
   84 }
   85 
   86 
   87 int HttpRequest::reset()
   88 {
   89     flags_ = 0;
   90 
   91     if (body_) {
   92         int res = body_->rewind();
   93         if (res != FZ_REPLY_CONTINUE) {
   94             return res;
   95         }
   96     }
   97     return FZ_REPLY_CONTINUE;
   98 }
   99 
  100 int HttpResponse::reset()
  101 {
  102     flags_ = 0;
  103     code_ = 0;
  104     headers_.clear();
  105 
  106     return FZ_REPLY_CONTINUE;
  107 }
  108 
  109 void RequestThrottler::throttle(std::string const& hostname, fz::datetime const& backoff)
  110 {
  111     if (hostname.empty() || !backoff) {
  112         return;
  113     }
  114 
  115     fz::scoped_lock l(mtx_);
  116 
  117     bool found{};
  118     auto now = fz::datetime::now();
  119     for (size_t i = 0; i < backoff_.size(); ) {
  120         auto & entry = backoff_[i];
  121         if (entry.first == hostname) {
  122             found = true;
  123             if (entry.second < backoff) {
  124                 entry.second = backoff;
  125             }
  126         }
  127         if (entry.second < now) {
  128             backoff_[i] = std::move(backoff_.back());
  129             backoff_.pop_back();
  130         }
  131         else {
  132             ++i;
  133         }
  134     }
  135     if (!found) {
  136         backoff_.emplace_back(hostname, backoff);
  137     }
  138 }
  139 
  140 fz::duration RequestThrottler::get_throttle(std::string const& hostname)
  141 {
  142     fz::scoped_lock l(mtx_);
  143 
  144     fz::duration ret;
  145 
  146     auto now = fz::datetime::now();
  147     for (size_t i = 0; i < backoff_.size(); ) {
  148         auto & entry = backoff_[i];
  149         if (entry.second < now) {
  150             backoff_[i] = std::move(backoff_.back());
  151             backoff_.pop_back();
  152         }
  153         else {
  154             if (entry.first == hostname) {
  155                 ret = entry.second - now;
  156             }
  157             ++i;
  158         }
  159     }
  160 
  161     return ret;
  162 }
  163 
  164 RequestThrottler CHttpControlSocket::throttler_;
  165 
  166 CHttpControlSocket::CHttpControlSocket(CFileZillaEnginePrivate & engine)
  167     : CRealControlSocket(engine)
  168 {
  169 }
  170 
  171 CHttpControlSocket::~CHttpControlSocket()
  172 {
  173     remove_handler();
  174     DoClose();
  175 }
  176 
  177 bool CHttpControlSocket::SetAsyncRequestReply(CAsyncRequestNotification *pNotification)
  178 {
  179     log(logmsg::debug_verbose, L"CHttpControlSocket::SetAsyncRequestReply");
  180 
  181     switch (pNotification->GetRequestID())
  182     {
  183     case reqId_fileexists:
  184         {
  185             if (operations_.back()->opId != Command::transfer) {
  186                 log(logmsg::debug_info, L"No or invalid operation in progress, ignoring request reply %f", pNotification->GetRequestID());
  187                 return false;
  188             }
  189 
  190             CFileExistsNotification *pFileExistsNotification = static_cast<CFileExistsNotification *>(pNotification);
  191             return SetFileExistsAction(pFileExistsNotification);
  192         }
  193         break;
  194     case reqId_certificate:
  195         {
  196             if (!tls_layer_ || tls_layer_->get_state() != fz::socket_state::connecting) {
  197                 log(logmsg::debug_info, L"No or invalid operation in progress, ignoring request reply %d", pNotification->GetRequestID());
  198                 return false;
  199             }
  200 
  201             CCertificateNotification* pCertificateNotification = static_cast<CCertificateNotification *>(pNotification);
  202             tls_layer_->set_verification_result(pCertificateNotification->trusted_);
  203         }
  204         break;
  205     default:
  206         log(logmsg::debug_warning, L"Unknown request %d", pNotification->GetRequestID());
  207         ResetOperation(FZ_REPLY_INTERNALERROR);
  208         return false;
  209     }
  210 
  211     return true;
  212 }
  213 
  214 
  215 void CHttpControlSocket::OnReceive()
  216 {
  217     if (operations_.empty() || operations_.back()->opId != PrivCommand::http_request) {
  218         uint8_t buffer;
  219         int error{};
  220         int read = active_layer_->read(&buffer, 1, error);
  221         if (!read) {
  222             log(logmsg::debug_warning, L"Idle socket got closed");
  223             ResetSocket();
  224         }
  225         else if (read == -1) {
  226             if (error != EAGAIN) {
  227                 log(logmsg::debug_warning, L"OnReceive called while not processing http request. Reading fails with error %d, closing socket.", error);
  228                 ResetSocket();
  229             }
  230         }
  231         else if (read) {
  232             log(logmsg::debug_warning, L"Server sent data while not in an active HTTP request, closing socket.");
  233             ResetSocket();
  234         }
  235         return;
  236     }
  237 
  238     int res = static_cast<CHttpRequestOpData&>(*operations_.back()).OnReceive();
  239     if (res == FZ_REPLY_CONTINUE) {
  240         SendNextCommand();
  241     }
  242     else if (res != FZ_REPLY_WOULDBLOCK) {
  243         ResetOperation(res);
  244     }
  245 }
  246 
  247 void CHttpControlSocket::OnConnect()
  248 {
  249     if (operations_.empty() || operations_.back()->opId != PrivCommand::http_connect) {
  250         log(logmsg::debug_warning, L"Discarding stale OnConnect");
  251         return;
  252     }
  253 
  254     socket_->set_flags(fz::socket::flag_nodelay, true);
  255 
  256     auto & data = static_cast<CHttpInternalConnectOpData &>(*operations_.back());
  257 
  258     if (data.tls_) {
  259         if (!tls_layer_) {
  260             log(logmsg::status, _("Connection established, initializing TLS..."));
  261 
  262             tls_layer_ = std::make_unique<fz::tls_layer>(event_loop_, this, *active_layer_, &engine_.GetContext().GetTlsSystemTrustStore(), logger_);
  263             active_layer_ = tls_layer_.get();
  264 
  265             if (!tls_layer_->client_handshake(&data)) {
  266                 DoClose();
  267             }
  268         }
  269         else {
  270             log(logmsg::status, _("TLS connection established, sending HTTP request"));
  271             ResetOperation(FZ_REPLY_OK);
  272         }
  273     }
  274     else {
  275         log(logmsg::status, _("Connection established, sending HTTP request"));
  276         ResetOperation(FZ_REPLY_OK);
  277     }
  278 }
  279 
  280 void CHttpControlSocket::FileTransfer(std::wstring const& localFile, CServerPath const& remotePath,
  281                                     std::wstring const& remoteFile, bool download,
  282                                     CFileTransferCommand::t_transferSettings const& settings)
  283 {
  284     log(logmsg::debug_verbose, L"CHttpControlSocket::FileTransfer()");
  285 
  286     if (download) {
  287         log(logmsg::status, _("Downloading %s"), remotePath.FormatFilename(remoteFile));
  288     }
  289 
  290     Push(std::make_unique<CHttpFileTransferOpData>(*this, download, localFile, remoteFile, remotePath, settings));
  291 }
  292 
  293 void CHttpControlSocket::FileTransfer(CHttpRequestCommand const& command)
  294 {
  295     log(logmsg::debug_verbose, L"CHttpControlSocket::FileTransfer()");
  296 
  297     log(logmsg::status, _("Requesting %s"), command.uri_.to_string());
  298 
  299     Push(std::make_unique<CHttpFileTransferOpData>(*this, command.uri_, command.verb_, command.body_));
  300 }
  301 
  302 void CHttpControlSocket::Request(std::shared_ptr<HttpRequestResponseInterface> const& request)
  303 {
  304     log(logmsg::debug_verbose, L"CHttpControlSocket::Request()");
  305 
  306     if (!request) {
  307         log(logmsg::debug_warning, L"Dropping null request");
  308         return;
  309     }
  310 
  311     auto op = dynamic_cast<CHttpRequestOpData*>(operations_.empty() ? nullptr : operations_.back().get());
  312     if (op) {
  313         op->AddRequest(request);
  314     }
  315     else {
  316         Push(std::make_unique<CHttpRequestOpData>(*this, request));
  317     }
  318 }
  319 
  320 void CHttpControlSocket::Request(std::deque<std::shared_ptr<HttpRequestResponseInterface>> && requests)
  321 {
  322     log(logmsg::debug_verbose, L"CHttpControlSocket::Request()");
  323     Push(std::make_unique<CHttpRequestOpData>(*this, std::move(requests)));
  324 }
  325 
  326 int CHttpControlSocket::InternalConnect(std::wstring const& host, unsigned short port, bool tls, bool allowDisconnect)
  327 {
  328     log(logmsg::debug_verbose, L"CHttpControlSocket::InternalConnect()");
  329 
  330     if (!Connected()) {
  331         return FZ_REPLY_INTERNALERROR;
  332     }
  333 
  334     if (active_layer_) {
  335         if (host == connected_host_ && port == connected_port_ && tls == connected_tls_) {
  336             log(logmsg::debug_verbose, L"Reusing an existing connection");
  337             return FZ_REPLY_OK;
  338         }
  339         if (!allowDisconnect) {
  340             return FZ_REPLY_WOULDBLOCK;
  341         }
  342     }
  343 
  344     ResetSocket();
  345     connected_host_ = host;
  346     connected_port_ = port;
  347     connected_tls_ = tls;
  348     Push(std::make_unique<CHttpInternalConnectOpData>(*this, ConvertDomainName(host), port, tls));
  349 
  350     return FZ_REPLY_CONTINUE;
  351 }
  352 
  353 void CHttpControlSocket::OnSocketError(int error)
  354 {
  355     log(logmsg::debug_verbose, L"CHttpControlSocket::OnClose(%d)", error);
  356 
  357     if (operations_.empty() || (operations_.back()->opId != PrivCommand::http_connect && operations_.back()->opId != PrivCommand::http_request)) {
  358         log(logmsg::debug_warning, L"Idle socket got closed");
  359         ResetSocket();
  360         return;
  361     }
  362 
  363     log(logmsg::error, _("Disconnected from server: %s"), fz::socket_error_description(error));
  364     ResetOperation(FZ_REPLY_ERROR | FZ_REPLY_DISCONNECTED);
  365 }
  366 
  367 void CHttpControlSocket::ResetSocket()
  368 {
  369     log(logmsg::debug_verbose, L"CHttpControlSocket::ResetSocket()");
  370 
  371     active_layer_ = nullptr;
  372 
  373     tls_layer_.reset();
  374 
  375     CRealControlSocket::ResetSocket();
  376 }
  377 
  378 int CHttpControlSocket::Disconnect()
  379 {
  380     DoClose();
  381     return FZ_REPLY_OK;
  382 }
  383 
  384 void CHttpControlSocket::Connect(CServer const& server, Credentials const& credentials)
  385 {
  386     currentServer_ = server;
  387     credentials_ = credentials;
  388     Push(std::make_unique<CHttpConnectOpData>(*this));
  389 }
  390 
  391 int CHttpControlSocket::OnSend()
  392 {
  393     int res = CRealControlSocket::OnSend();
  394     if (res == FZ_REPLY_CONTINUE) {
  395         if (!operations_.empty() && operations_.back()->opId == PrivCommand::http_request && (operations_.back()->opState & request_send_mask)) {
  396             return SendNextCommand();
  397         }
  398     }
  399     return res;
  400 }