Skip to content
Snippets Groups Projects
tcp_server_socket.cpp 3.96 KiB
Newer Older
/*
 * tcp_server_socket.cpp
 *
 *  Created on: 02.01.2021
 *      Author: doralitze
 */
#include "net/tcp_server_socket.hpp"

#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <functional>

#include "macros.hpp"
#include "net/client_factory.hpp"
#include "net/socketaddress.hpp"
#include "net/sock_address_factory.hpp"
#include "net/socket_utils.hpp"
#include "net/tcp_client.hpp"
namespace rmrf::net {
Benny Baumann's avatar
Benny Baumann committed

static auto_fd create_tcp_server_socket(const socketaddr& socket_identifier) {
Benny Baumann's avatar
Benny Baumann committed
    auto_fd socket_fd{socket(socket_identifier.family(), SOCK_STREAM, 0)};

    if (!socket_fd.valid()) {
        // TODO implement propper error handling
        throw netio_exception("Failed to create socket fd.");
    }

    if (auto error = bind(socket_fd.get(), socket_identifier.ptr(), socket_identifier.size()); error != 0) {
        std::string msg = "Failed to bind to all addresses (FIXME). Errorcode: " + std::to_string(error);

        if (socket_identifier.family() == AF_INET6) {
            sockaddr_in* inptr = (sockaddr_in*) socket_identifier.ptr();
            const auto port = ntohs(inptr->sin_port);

            if (port < 1024) {
                msg += "\nYou tried to bind to a port smaller than 1024. Are you root?";
Benny Baumann's avatar
Benny Baumann committed
        } else if (socket_identifier.family() == AF_INET) {
            sockaddr_in6* inptr = (sockaddr_in6*) socket_identifier.ptr();
            const auto port = ntohs(inptr->sin6_port);
Benny Baumann's avatar
Benny Baumann committed
            if (port < 1024) {
                msg += "\nYou tried to bind to a port smaller than 1024. Are you root?";
            }
Benny Baumann's avatar
Benny Baumann committed

        throw netio_exception(msg);
    }

    make_socket_nonblocking(socket_fd);

    if (listen(socket_fd.get(), 128) == -1) {
Benny Baumann's avatar
Benny Baumann committed
        throw netio_exception("Failed to enable listening mode for raw socket");
Benny Baumann's avatar
Benny Baumann committed
    return socket_fd;
}

tcp_server_socket::tcp_server_socket(
    const socketaddr& socket_identifier,
    async_server_socket::accept_handler_type client_listener
) :
    async_server_socket(std::move(create_tcp_server_socket(socket_identifier)))
{
    this->set_accept_handler(client_listener);
}

static inline socketaddr get_ipv6_socketaddr(const uint16_t port) {
Benny Baumann's avatar
Benny Baumann committed
    sockaddr_in6 addr;
    addr.sin6_family = AF_INET6;
    addr.sin6_port = htons(port);
    addr.sin6_addr = IN6ADDR_ANY_INIT;
    socketaddr sa{addr};
    return sa;
Benny Baumann's avatar
Benny Baumann committed
tcp_server_socket::tcp_server_socket(
    const uint16_t port,
    async_server_socket::accept_handler_type client_listener_
Benny Baumann's avatar
Benny Baumann committed
) :
    tcp_server_socket{get_ipv6_socketaddr(port), client_listener_}
{}
tcp_server_socket::tcp_server_socket(
    const std::string& interface_description,
    const std::string& port,
    async_server_socket::accept_handler_type client_listener_
) : tcp_server_socket{get_first_general_socketaddr(interface_description, port, socket_t::TCP), client_listener_} {}

std::shared_ptr<connection_client> tcp_server_socket::await_raw_socket_incomming(const auto_fd& server_socket) {
    struct sockaddr_storage client_addr_raw;
    socklen_t client_len = sizeof(client_addr_raw);
    int client_fd_raw = accept(server_socket.get(), (struct sockaddr*) &client_addr_raw, &client_len);
    const socketaddr client_address{client_addr_raw};
Benny Baumann's avatar
Benny Baumann committed
    if (client_fd_raw < 0) {
        throw netio_exception("Unable to bind incoming client");
    }
Benny Baumann's avatar
Benny Baumann committed

    auto client_socket = auto_fd(client_fd_raw);
    make_socket_nonblocking(client_socket);
    if (this->is_low_latency_mode_enabled()) {
        int one = 1;
        setsockopt(client_socket.get(), IPPROTO_TCP, TCP_NODELAY, &one, sizeof(one));
        setsockopt(client_socket.get(), IPPROTO_TCP, TCP_QUICKACK, &one, sizeof(one));
Benny Baumann's avatar
Benny Baumann committed

    return std::make_shared<tcp_client>(
        this->get_locked_destructor_callback(),
        std::move(client_socket),
        get_own_address_after_connect(client_socket),
        client_address);