summaryrefslogtreecommitdiff
path: root/libs/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'libs/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp')
-rw-r--r--libs/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp160
1 files changed, 160 insertions, 0 deletions
diff --git a/libs/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp b/libs/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp
new file mode 100644
index 0000000000..ead43a3d4b
--- /dev/null
+++ b/libs/tdlib/td/tdutils/td/utils/port/ServerSocketFd.cpp
@@ -0,0 +1,160 @@
+//
+// Copyright Aliaksei Levin (levlam@telegram.org), Arseny Smirnov (arseny30@gmail.com) 2014-2018
+//
+// Distributed under the Boost Software License, Version 1.0. (See accompanying
+// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+//
+#include "td/utils/port/ServerSocketFd.h"
+
+#include "td/utils/port/config.h"
+
+#include "td/utils/logging.h"
+#include "td/utils/port/IPAddress.h"
+
+#if TD_PORT_POSIX
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#endif
+
+namespace td {
+
+Result<ServerSocketFd> ServerSocketFd::open(int32 port, CSlice addr) {
+ ServerSocketFd socket;
+ TRY_STATUS(socket.init(port, addr));
+ return std::move(socket);
+}
+
+const Fd &ServerSocketFd::get_fd() const {
+ return fd_;
+}
+
+Fd &ServerSocketFd::get_fd() {
+ return fd_;
+}
+
+int32 ServerSocketFd::get_flags() const {
+ return fd_.get_flags();
+}
+
+Status ServerSocketFd::get_pending_error() {
+ return fd_.get_pending_error();
+}
+
+Result<SocketFd> ServerSocketFd::accept() {
+#if TD_PORT_POSIX
+ sockaddr_storage addr;
+ socklen_t addr_len = sizeof(addr);
+ int native_fd = fd_.get_native_fd();
+ int r_fd = skip_eintr([&] { return ::accept(native_fd, reinterpret_cast<sockaddr *>(&addr), &addr_len); });
+ auto accept_errno = errno;
+ if (r_fd >= 0) {
+ return SocketFd::from_native_fd(r_fd);
+ }
+
+ if (accept_errno == EAGAIN
+#if EAGAIN != EWOULDBLOCK
+ || accept_errno == EWOULDBLOCK
+#endif
+ ) {
+ fd_.clear_flags(Fd::Read);
+ return Status::Error(-1, "Operation would block");
+ }
+
+ auto error = Status::PosixError(accept_errno, PSLICE() << "Accept from [fd = " << native_fd << "] has failed");
+ switch (accept_errno) {
+ case EBADF:
+ case EFAULT:
+ case EINVAL:
+ case ENOTSOCK:
+ case EOPNOTSUPP:
+ LOG(FATAL) << error;
+ UNREACHABLE();
+ break;
+ default:
+ LOG(ERROR) << error;
+ // fallthrough
+ case EMFILE:
+ case ENFILE:
+ case ECONNABORTED: //???
+ fd_.clear_flags(Fd::Read);
+ fd_.update_flags(Fd::Close);
+ return std::move(error);
+ }
+#elif TD_PORT_WINDOWS
+ TRY_RESULT(socket_fd, fd_.accept());
+ return SocketFd(std::move(socket_fd));
+#endif
+}
+
+void ServerSocketFd::close() {
+ fd_.close();
+}
+
+bool ServerSocketFd::empty() const {
+ return fd_.empty();
+}
+
+Status ServerSocketFd::init(int32 port, CSlice addr) {
+ IPAddress address;
+ TRY_STATUS(address.init_ipv4_port(addr, port));
+ auto fd = socket(address.get_address_family(), SOCK_STREAM, 0);
+#if TD_PORT_POSIX
+ if (fd == -1) {
+#elif TD_PORT_WINDOWS
+ if (fd == INVALID_SOCKET) {
+#endif
+ return OS_SOCKET_ERROR("Failed to create a socket");
+ }
+ auto fd_quard = ScopeExit() + [fd]() {
+#if TD_PORT_POSIX
+ ::close(fd);
+#elif TD_PORT_WINDOWS
+ ::closesocket(fd);
+#endif
+ };
+
+ TRY_STATUS(detail::set_native_socket_is_blocking(fd, false));
+
+ linger ling = {0, 0};
+#if TD_PORT_POSIX
+ int flags = 1;
+#ifdef SO_REUSEPORT
+ setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<const char *>(&flags), sizeof(flags));
+#endif
+#elif TD_PORT_WINDOWS
+ BOOL flags = TRUE;
+#endif
+ setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, reinterpret_cast<const char *>(&flags), sizeof(flags));
+ setsockopt(fd, SOL_SOCKET, SO_LINGER, reinterpret_cast<const char *>(&ling), sizeof(ling));
+ setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<const char *>(&flags), sizeof(flags));
+
+ int e_bind = bind(fd, address.get_sockaddr(), static_cast<socklen_t>(address.get_sockaddr_len()));
+ if (e_bind != 0) {
+ return OS_SOCKET_ERROR("Failed to bind a socket");
+ }
+
+ // TODO: magic constant
+ int e_listen = listen(fd, 8192);
+ if (e_listen != 0) {
+ return OS_SOCKET_ERROR("Failed to listen on a socket");
+ }
+
+#if TD_PORT_POSIX
+ fd_ = Fd(fd, Fd::Mode::Owner);
+#elif TD_PORT_WINDOWS
+ fd_ = Fd::create_server_socket_fd(fd, address.get_address_family());
+#endif
+
+ fd_quard.dismiss();
+ return Status::OK();
+}
+
+} // namespace td