diff --git a/src/Makefile.am b/src/Makefile.am index 89280276..097872a6 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -172,8 +172,6 @@ libsub_root_a_SOURCES = \ utils/list_focus.h \ utils/lockfile.cc \ utils/lockfile.h \ - utils/socket_fd.cc \ - utils/socket_fd.h \ \ command_download.cc \ command_dynamic.cc \ diff --git a/src/rpc/scgi.cc b/src/rpc/scgi.cc index 0c81586b..db3ccf6c 100644 --- a/src/rpc/scgi.cc +++ b/src/rpc/scgi.cc @@ -1,6 +1,7 @@ #include "config.h" #include +#include #include #include #include @@ -12,7 +13,6 @@ #include "control.h" #include "globals.h" #include "rpc/scgi_task.h" -#include "utils/socket_fd.h" // TODO: Figure out why moving this to the top causes a build error. #include "rpc/scgi.h" @@ -20,30 +20,26 @@ namespace rpc { SCgi::~SCgi() { - if (!get_fd().is_valid()) - return; - - for (SCgiTask* itr = m_task, *last = m_task + max_tasks; itr != last; ++itr) - if (itr->is_open()) - itr->close(); - - deactivate(); - torrent::connection_manager()->dec_socket_count(); - - get_fd().close(); - get_fd().clear(); - - if (!m_path.empty()) - ::unlink(m_path.c_str()); + assert(!is_open() && "SCgi::~SCgi() called while open"); } void -SCgi::open_port(void* sa, unsigned int length, bool dontRoute) { - if (!get_fd().open_stream() || - (dontRoute && !get_fd().set_dont_route(true))) +SCgi::open_port(sockaddr* sa, unsigned int length, bool dont_route) { + int fd = torrent::fd_open_family(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address, + reinterpret_cast(sa)->sa_family); + + if (fd == -1) throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno))); - open(sa, length); + if (dont_route && !torrent::fd_set_dont_route(fd, true)) { + torrent::fd_close(fd); + throw torrent::resource_error("Could not set socket option IP_DONTROUTE: " + std::string(std::strerror(errno))); + } + + set_file_descriptor(fd); + open(reinterpret_cast(sa), length); + + torrent::connection_manager()->inc_socket_count(); } void @@ -51,45 +47,42 @@ SCgi::open_named(const std::string& filename) { if (filename.empty() || filename.size() > 4096) throw torrent::resource_error("Invalid filename length."); - auto buffer = std::make_unique(sizeof(sockaddr_un) + filename.size()); + auto buffer = std::make_unique(sizeof(sockaddr_un) + filename.size() + 1); + sockaddr_un* sa = reinterpret_cast(buffer.get()); - -#ifdef __sun__ - sa->sun_family = AF_UNIX; -#else sa->sun_family = AF_LOCAL; -#endif - std::memcpy(sa->sun_path, filename.c_str(), filename.size() + 1); - if (!get_fd().open_local()) - throw torrent::resource_error("Could not open socket for listening."); + int fd = torrent::fd_open_local(torrent::fd_flag_stream | torrent::fd_flag_nonblock | torrent::fd_flag_reuse_address); + + if (fd == -1) + throw torrent::resource_error("Could not open socket for listening: " + std::string(std::strerror(errno))); + + set_file_descriptor(fd); + open(reinterpret_cast(sa), offsetof(struct sockaddr_un, sun_path) + filename.size() + 1); + + torrent::connection_manager()->inc_socket_count(); - open(sa, offsetof(struct sockaddr_un, sun_path) + filename.size() + 1); m_path = filename; } void -SCgi::open(void* sa, unsigned int length) { +SCgi::open(sockaddr* sa, unsigned int length) { try { - if (!get_fd().set_nonblock() || - !get_fd().set_reuse_address(true) || - !get_fd().bind_sa(reinterpret_cast(sa), length) || - !get_fd().listen(max_tasks)) + if (::bind(file_descriptor(), sa, length) == -1) + throw torrent::resource_error("Could not bind socket for listening: " + std::string(std::strerror(errno))); + + if (!torrent::fd_listen(file_descriptor(), max_tasks)) throw torrent::resource_error("Could not prepare socket for listening: " + std::string(std::strerror(errno))); - torrent::connection_manager()->inc_socket_count(); - } catch (torrent::resource_error& e) { - get_fd().close(); - get_fd().clear(); + torrent::fd_close(file_descriptor()); + set_file_descriptor(-1); throw e; } } -// TODO: Verify this is run in correct thread, also only ever call poll methods from thread_self. - void SCgi::activate() { assert(torrent::this_thread::thread() == scgi_thread::thread()); @@ -100,16 +93,31 @@ SCgi::activate() { } void -SCgi::deactivate() { +SCgi::stop() { assert(torrent::this_thread::thread() == scgi_thread::thread()); + if (!is_open()) + return; + + for (SCgiTask* itr = m_task, *last = m_task + max_tasks; itr != last; ++itr) + if (itr->is_open()) + itr->close(); + torrent::this_thread::poll()->remove_and_close(this); + + torrent::fd_close(file_descriptor()); + set_file_descriptor(-1); + + torrent::connection_manager()->dec_socket_count(); + + if (!m_path.empty()) + ::unlink(m_path.c_str()); } void SCgi::event_read() { while (true) { - int fd = torrent::fd_accept(get_fd().get_fd()); + int fd = torrent::fd_accept(file_descriptor()); if (fd == -1) { if (errno == EAGAIN || errno == EWOULDBLOCK) diff --git a/src/rpc/scgi.h b/src/rpc/scgi.h index 0e97c983..51afe807 100644 --- a/src/rpc/scgi.h +++ b/src/rpc/scgi.h @@ -5,11 +5,6 @@ #include #include "rpc/scgi_task.h" -#include "utils/socket_fd.h" - -namespace utils { - class SocketFd; -} namespace rpc { @@ -21,11 +16,12 @@ public: const char* type_name() const override { return "scgi"; } - void open_port(void* sa, unsigned int length, bool dontRoute); + void open_port(sockaddr* sa, unsigned int length, bool dont_route); void open_named(const std::string& filename); void activate(); - void deactivate(); + + void stop(); const std::string& path() const { return m_path; } @@ -36,10 +32,8 @@ public: void event_write() override; void event_error() override; - utils::SocketFd& get_fd() { return *reinterpret_cast(&m_fileDesc); } - private: - void open(void* sa, unsigned int length); + void open(sockaddr* sa, unsigned int length); std::string m_path; int m_logFd{-1}; diff --git a/src/rpc/scgi_task.cc b/src/rpc/scgi_task.cc index 6dd7924a..131fe720 100644 --- a/src/rpc/scgi_task.cc +++ b/src/rpc/scgi_task.cc @@ -3,11 +3,13 @@ #include "rpc/scgi_task.h" #include +#include #include #include #include #include #include +#include #include #include #include @@ -16,7 +18,6 @@ #include "globals.h" #include "scgi.h" #include "rpc/parse_commands.h" -#include "utils/socket_fd.h" namespace rpc { @@ -24,10 +25,12 @@ void SCgiTask::open(SCgi* parent, int fd) { m_parent = parent; m_fileDesc = fd; - m_buffer = new char[default_buffer_size + 1]; + + m_buffer.reset(new char[default_buffer_size + 1]); + m_buffer_size = default_buffer_size; - m_position = m_buffer; - m_body = NULL; + m_position = m_buffer.get(); + m_body = nullptr; torrent::this_thread::poll()->open(this); torrent::this_thread::poll()->insert_read(this); @@ -36,7 +39,7 @@ SCgiTask::open(SCgi* parent, int fd) { void SCgiTask::close() { - if (!get_fd().is_valid()) + if (!is_open()) return; torrent::main_thread::thread()->cancel_callback_and_wait(this); @@ -44,18 +47,17 @@ SCgiTask::close() { torrent::this_thread::poll()->remove_and_close(this); - get_fd().close(); - get_fd().clear(); + torrent::fd_close(file_descriptor()); + set_file_descriptor(-1); auto lock = std::lock_guard(m_result_mutex); - delete[] m_buffer; - m_buffer = NULL; + m_buffer = nullptr; } void SCgiTask::event_read() { - int bytes = ::recv(m_fileDesc, m_position, m_buffer_size - (m_position - m_buffer), 0); + int bytes = ::recv(m_fileDesc, m_position, m_buffer_size - (m_position - m_buffer.get()), 0); if (bytes <= 0) { if (bytes == 0 || !(errno == EAGAIN || errno == EINTR)) @@ -73,14 +75,14 @@ SCgiTask::event_read() { // receive all the data we need the first time. char* current; - int header_size = strtol(m_buffer, ¤t, 0); + int header_size = strtol(m_buffer.get(), ¤t, 0); if (current == m_position) return; // If the request doesn't start with an integer or if it didn't // end in ':', then close the connection. - if (current == m_buffer || *current != ':' || header_size < 17 || header_size > max_header_size) + if (current == m_buffer.get() || *current != ':' || header_size < 17 || header_size > max_header_size) goto event_read_failed; if (std::distance(++current, m_position) < header_size + 1) @@ -135,7 +137,7 @@ SCgiTask::event_read() { goto event_read_failed; m_body = current + 1; - header_size = std::distance(m_buffer, m_body); + header_size = std::distance(m_buffer.get(), m_body); if (!detect_content_type(content_type)) goto event_read_failed; @@ -146,19 +148,19 @@ SCgiTask::event_read() { } else if ((unsigned int)content_length <= default_buffer_size) { m_buffer_size = content_length; - std::memmove(m_buffer, m_body, std::distance(m_body, m_position)); - m_position = m_buffer + std::distance(m_body, m_position); - m_body = m_buffer; + std::memmove(m_buffer.get(), m_body, std::distance(m_body, m_position)); + m_position = m_buffer.get() + std::distance(m_body, m_position); + m_body = m_buffer.get(); } else { realloc_buffer((m_buffer_size = content_length) + 1, m_body, std::distance(m_body, m_position)); - m_position = m_buffer + std::distance(m_body, m_position); - m_body = m_buffer; + m_position = m_buffer.get() + std::distance(m_body, m_position); + m_body = m_buffer.get(); } } - if ((unsigned int)std::distance(m_buffer, m_position) != m_buffer_size) + if ((unsigned int)std::distance(m_buffer.get(), m_position) != m_buffer_size) return; torrent::this_thread::poll()->remove_read(this); @@ -168,13 +170,13 @@ SCgiTask::event_read() { // Clean up logging, this is just plain ugly... // write(m_logFd, "\n---\n", sizeof("\n---\n")); - result = write(m_parent->log_fd(), m_buffer, m_buffer_size); - result = write(m_parent->log_fd(), "\n---\n", sizeof("\n---\n")); + result = ::write(m_parent->log_fd(), m_buffer.get(), m_buffer_size); + result = ::write(m_parent->log_fd(), "\n---\n", sizeof("\n---\n")); } - lt_log_print_dump(torrent::LOG_RPC_DUMP, m_body, m_buffer_size - std::distance(m_buffer, m_body), "scgi", "RPC read.", 0); + lt_log_print_dump(torrent::LOG_RPC_DUMP, m_body, m_buffer_size - std::distance(m_buffer.get(), m_body), "scgi", "RPC read.", 0); - receive_call(m_body, m_buffer_size - std::distance(m_buffer, m_body)); + receive_call(m_body, m_buffer_size - std::distance(m_buffer.get(), m_body)); return; event_read_failed: @@ -184,13 +186,7 @@ event_read_failed: void SCgiTask::event_write() { -// Apple and Solaris do not support MSG_NOSIGNAL, -// so disable this fix until we find a better solution -#if defined(__APPLE__) || defined(__sun__) - int bytes = ::send(m_fileDesc, m_position, m_buffer_size, 0); -#else int bytes = ::send(m_fileDesc, m_position, m_buffer_size, MSG_NOSIGNAL); -#endif if (bytes == -1) { if (!(errno == EAGAIN || errno == EINTR)) @@ -248,11 +244,11 @@ SCgiTask::detect_content_type(const std::string& content_type) { // If bufferSize is zero then memcpy won't do anything. void SCgiTask::realloc_buffer(uint32_t size, const char* buffer, uint32_t bufferSize) { - char* tmp = new char[size]; + auto tmp = new char[size]; std::memcpy(tmp, buffer, bufferSize); - ::free(m_buffer); - m_buffer = tmp; + + m_buffer.reset(tmp); } void @@ -317,20 +313,20 @@ SCgiTask::receive_write(const char* buffer, uint32_t length) { : "Status: 200 OK\r\nContent-Type: text/xml\r\nContent-Length: %i\r\n\r\n"; // Who ever bothers to check the return value? - int headerSize = snprintf(m_buffer, m_buffer_size, header, length); + int headerSize = snprintf(m_buffer.get(), m_buffer_size, header, length); - m_position = m_buffer; + m_position = m_buffer.get(); m_buffer_size = length + headerSize; - std::memcpy(m_buffer + headerSize, buffer, length); + std::memcpy(m_buffer.get() + headerSize, buffer, length); if (m_parent->log_fd() >= 0) { [[maybe_unused]] int result; - result = write(m_parent->log_fd(), m_buffer, m_buffer_size); + result = write(m_parent->log_fd(), m_buffer.get(), m_buffer_size); result = write(m_parent->log_fd(), "\n---\n", sizeof("\n---\n")); } - lt_log_print_dump(torrent::LOG_RPC_DUMP, m_buffer, m_buffer_size, "scgi", "RPC write.", 0); + lt_log_print_dump(torrent::LOG_RPC_DUMP, m_buffer.get(), m_buffer_size, "scgi", "RPC write.", 0); } } // namespace rpc diff --git a/src/rpc/scgi_task.h b/src/rpc/scgi_task.h index 0996e0a6..b1ce5ce7 100644 --- a/src/rpc/scgi_task.h +++ b/src/rpc/scgi_task.h @@ -5,10 +5,6 @@ #include #include -namespace utils { - class SocketFd; -} - namespace rpc { class SCgi; @@ -37,8 +33,6 @@ public: void event_write() override; void event_error() override; - utils::SocketFd& get_fd() { return *reinterpret_cast(&m_fileDesc); } - private: bool detect_content_type(const std::string& content_type); void realloc_buffer(uint32_t size, const char* buffer, uint32_t bufferSize); @@ -46,13 +40,13 @@ private: void receive_call(const char* buffer, uint32_t length); void receive_write(const char* buffer, uint32_t length); - SCgi* m_parent; + SCgi* m_parent{}; std::mutex m_result_mutex; - char* m_buffer{nullptr}; - char* m_position{nullptr}; - char* m_body{nullptr}; + std::unique_ptr m_buffer; + char* m_position{}; + char* m_body{}; unsigned int m_buffer_size{0}; diff --git a/src/scgi/thread_scgi.cc b/src/scgi/thread_scgi.cc index 6c33b392..7e09d373 100644 --- a/src/scgi/thread_scgi.cc +++ b/src/scgi/thread_scgi.cc @@ -41,7 +41,7 @@ ThreadScgi::thread_scgi() { void ThreadScgi::cleanup_thread() { if (m_scgi != nullptr) - m_scgi.load()->deactivate(); + m_scgi.load()->stop(); } rpc::SCgi* @@ -49,6 +49,8 @@ ThreadScgi::scgi() { return m_scgi; } +// TODO: Disable changing SCGI once set? + bool ThreadScgi::set_scgi(rpc::SCgi* scgi) { rpc::SCgi* expected = nullptr; @@ -59,7 +61,7 @@ ThreadScgi::set_scgi(rpc::SCgi* scgi) { change_rpc_log(); callback(nullptr, [this]() { - if (m_scgi == NULL) + if (m_scgi == nullptr) throw torrent::internal_error("Tried to start SCGI but object was not present."); m_scgi.load()->activate(); @@ -78,7 +80,7 @@ ThreadScgi::set_rpc_log(const std::string& filename) { void ThreadScgi::change_rpc_log() { - if (scgi() == NULL) + if (scgi() == nullptr) return; if (scgi()->log_fd() != -1) { diff --git a/src/utils/socket_fd.cc b/src/utils/socket_fd.cc deleted file mode 100644 index 515627ab..00000000 --- a/src/utils/socket_fd.cc +++ /dev/null @@ -1,159 +0,0 @@ -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "socket_fd.h" - -namespace utils { - -inline void -SocketFd::check_valid() const { - if (!is_valid()) - throw torrent::internal_error("SocketFd function called on an invalid fd."); -} - -bool -SocketFd::set_nonblock() { - check_valid(); - - return fcntl(m_fd, F_SETFL, O_NONBLOCK) == 0; -} - -bool -SocketFd::set_priority(priority_type p) { - check_valid(); - int opt = p; - - if (m_ipv6_socket) - return setsockopt(m_fd, IPPROTO_IPV6, IPV6_TCLASS, &opt, sizeof(opt)) == 0; - else - return setsockopt(m_fd, IPPROTO_IP, IP_TOS, &opt, sizeof(opt)) == 0; -} - -bool -SocketFd::set_reuse_address(bool state) { - check_valid(); - int opt = state; - - return setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) == 0; -} - -bool -SocketFd::set_dont_route(bool state) { - check_valid(); - int opt = state; - - return setsockopt(m_fd, SOL_SOCKET, SO_DONTROUTE, &opt, sizeof(opt)) == 0; -} - -// bool -// SocketFd::set_bind_to_device(const char* device) { -// check_valid(); -// struct ifreq ifr; -// strlcpy(ifr.ifr_name, device, IFNAMSIZ); - -// return setsockopt(m_fd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr)) == 0; -// } - -bool -SocketFd::set_send_buffer_size(uint32_t s) { - check_valid(); - int opt = s; - - return setsockopt(m_fd, SOL_SOCKET, SO_SNDBUF, &opt, sizeof(opt)) == 0; -} - -bool -SocketFd::set_receive_buffer_size(uint32_t s) { - check_valid(); - int opt = s; - - return setsockopt(m_fd, SOL_SOCKET, SO_RCVBUF, &opt, sizeof(opt)) == 0; -} - -int -SocketFd::get_error() const { - check_valid(); - - int err; - socklen_t length = sizeof(err); - - if (getsockopt(m_fd, SOL_SOCKET, SO_ERROR, &err, &length) == -1) - throw torrent::internal_error("SocketFd::get_error() could not get error"); - - return err; -} - -bool -SocketFd::open_stream() { - m_fd = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP); - - if (m_fd == -1) { - m_ipv6_socket = false; - return (m_fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) != -1; - } - - m_ipv6_socket = true; - - int zero = 0; - return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1; -} - -bool -SocketFd::open_datagram() { - m_fd = socket(AF_INET6, SOCK_DGRAM, 0); - if (m_fd == -1) { - m_ipv6_socket = false; - return (m_fd = socket(AF_INET, SOCK_DGRAM, 0)) != -1; - } - m_ipv6_socket = true; - - int zero = 0; - return setsockopt(m_fd, IPPROTO_IPV6, IPV6_V6ONLY, &zero, sizeof(zero)) != -1; -} - -bool -SocketFd::open_local() { - return (m_fd = socket(AF_LOCAL, SOCK_STREAM, 0)) != -1; -} - -void -SocketFd::close() { - if (::close(m_fd) && errno == EBADF) - throw torrent::internal_error("SocketFd::close() called on an invalid file descriptor"); -} - -bool -SocketFd::bind_sa(const sockaddr* sa, unsigned int length) { - check_valid(); - - if (m_ipv6_socket && sa->sa_family == AF_INET) { - if (length < sizeof(sockaddr_in)) - throw torrent::input_error("SocketFd::bind_sa: invalid sockaddr length for AF_INET"); - - auto mapped_sa = torrent::sin6_to_v4mapped_in(reinterpret_cast(sa)); - return !::bind(m_fd, reinterpret_cast(mapped_sa.get()), sizeof(sockaddr_in6)); - } - - return !::bind(m_fd, sa, length); -} - -bool -SocketFd::listen(int size) { - check_valid(); - - return !::listen(m_fd, size); -} - -} diff --git a/src/utils/socket_fd.h b/src/utils/socket_fd.h deleted file mode 100644 index 2efce097..00000000 --- a/src/utils/socket_fd.h +++ /dev/null @@ -1,55 +0,0 @@ -#ifndef RTORRENT_UTILS_SOCKET_FD_H -#define RTORRENT_UTILS_SOCKET_FD_H - -#include -#include -#include - -namespace utils { - -class SocketFd { -public: - typedef uint8_t priority_type; - - SocketFd() : m_fd(-1) {} - explicit SocketFd(int fd) : m_fd(fd) {} - - bool is_valid() const { return m_fd >= 0; } - - int get_fd() const { return m_fd; } - void set_fd(int fd) { m_fd = fd; } - - bool set_nonblock(); - bool set_reuse_address(bool state); - bool set_dont_route(bool state); - - bool set_bind_to_device(const char* device); - - bool set_priority(priority_type p); - - bool set_send_buffer_size(uint32_t s); - bool set_receive_buffer_size(uint32_t s); - - int get_error() const; - - bool open_stream(); - bool open_datagram(); - bool open_local(); - void close(); - - void clear() { m_fd = -1; } - - bool bind_sa(const sockaddr* sa, unsigned int length); - - bool listen(int size); - -private: - inline void check_valid() const; - - int m_fd; - bool m_ipv6_socket; -}; - -} - -#endif