Select Git revision
EmbeddedData.cpp
Socket.cpp 6.02 KiB
#include <utils/Exceptions.hpp>
#include <utils/Socket.hpp>
#include <utils/Messenger.hpp>
#include <arpa/inet.h>
#include <cstring>
#include <iostream>
#include <netdb.h>
#include <netinet/in.h>
#include <stdexcept>
#include <sys/socket.h>
#include <unistd.h>
class Socket::Internals
{
private:
int m_socket_fd;
sockaddr_in m_address;
const bool m_is_server_socket;
public:
friend Socket createServerSocket(int port_number);
friend Socket acceptClientSocket(const Socket& server);
friend Socket connectServerSocket(const std::string& server_name, int port_number);
friend std::ostream&
operator<<(std::ostream& os, const Socket::Internals& internals)
{
// This function's coverage is not performed since it's quite
// complex to create its various conditions
//
// LCOV_EXCL_START
char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV];
if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
sizeof(hbuf), sbuf, sizeof(sbuf), NI_NAMEREQD) == 0) {
os << hbuf << ':' << sbuf;
} else if (::getnameinfo(reinterpret_cast<const sockaddr*>(&internals.m_address), sizeof(internals.m_address), hbuf,
sizeof(hbuf), sbuf, sizeof(sbuf), NI_NUMERICHOST) == 0) {
if (std ::string{hbuf} == "0.0.0.0") {
if (::gethostname(hbuf, NI_MAXHOST) == 0) {
os << hbuf << ':' << sbuf;
} else {
os << "localhost:" << sbuf;
}
} else {
os << hbuf << ':' << sbuf;
}
} else {
os << "<unknown host>";
}
// LCOV_EXCL_STOP
return os;
}
bool
isServerSocket() const
{
return m_is_server_socket;
}
int
portNumber() const
{
return ntohs(m_address.sin_port);
}
int
fileDescriptor() const
{
return m_socket_fd;
}
Internals(const Internals&) = delete;
Internals(Internals&&) = delete;
Internals(bool is_server_socket = false) : m_is_server_socket{is_server_socket}
{
if (parallel::size() > 1) {
// LCOV_EXCL_START
throw NotImplementedError("Sockets are not managed in parallel");
// LCOV_EXCL_STOP
}
}
~Internals()
{
close(m_socket_fd);
}
};
std::ostream&
operator<<(std::ostream& os, const Socket& socket)
{
return os << *socket.m_internals;
}
int
Socket::portNumber() const
{
return m_internals->portNumber();
}
Socket
createServerSocket(int port_number)
{
auto p_socket_internals = std::make_shared<Socket::Internals>(true);
Socket::Internals& socket_internals = *p_socket_internals;
socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket_internals.m_socket_fd < 0) {
// This should never happen
throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE
}
socket_internals.m_address.sin_family = AF_INET;
socket_internals.m_address.sin_addr.s_addr = INADDR_ANY;
socket_internals.m_address.sin_port = htons(port_number);
int on = 1;
::setsockopt(socket_internals.m_socket_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
socklen_t length = sizeof(socket_internals.m_address);
if (::bind(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), length) < 0) {
throw NormalError(strerror(errno));
}
if (::getsockname(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&socket_internals.m_address), &length) ==
-1) {
// This should never happen
throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE
}
::listen(socket_internals.m_socket_fd, 1);
return Socket{p_socket_internals};
}
Socket
acceptClientSocket(const Socket& server)
{
auto p_socket_internals = std::make_shared<Socket::Internals>();
Socket::Internals& socket_internals = *p_socket_internals;
socklen_t address_lenght = sizeof(socket_internals.m_address);
socket_internals.m_socket_fd = ::accept(server.m_internals->m_socket_fd,
reinterpret_cast<sockaddr*>(&socket_internals.m_address), &address_lenght);
if (socket_internals.m_socket_fd < 0) {
// This should never happen
throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE
}
return Socket{p_socket_internals};
}
Socket
connectServerSocket(const std::string& server_name, int port_number)
{
auto p_socket_internals = std::make_shared<Socket::Internals>();
Socket::Internals& socket_internals = *p_socket_internals;
socket_internals.m_socket_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (socket_internals.m_socket_fd < 0) {
// This should never happen
throw UnexpectedError(strerror(errno)); // LCOV_EXCL_LINE
}
hostent* server = ::gethostbyname(server_name.c_str());
if (server == NULL) {
throw NormalError(strerror(errno));
}
sockaddr_in& serv_addr = socket_internals.m_address;
::memset(reinterpret_cast<char*>(&serv_addr), 0, sizeof(serv_addr));
serv_addr.sin_family = AF_INET;
::memcpy(reinterpret_cast<char*>(&serv_addr.sin_addr.s_addr), reinterpret_cast<char*>(server->h_addr),
server->h_length);
serv_addr.sin_port = htons(port_number);
if (::connect(socket_internals.m_socket_fd, reinterpret_cast<sockaddr*>(&serv_addr), sizeof(serv_addr))) {
throw NormalError(strerror(errno));
}
return Socket{p_socket_internals};
}
void
Socket::_write(const char* data, const size_t lenght) const
{
if (this->m_internals->isServerSocket()) {
throw NormalError("Server cannot write to server socket");
}
if (::write(this->m_internals->fileDescriptor(), data, lenght) < 0) {
// Quite complex to test
throw NormalError(strerror(errno)); // LCOV_EXCL_LINE
}
}
void
Socket::_read(char* data, const size_t length) const
{
if (this->m_internals->isServerSocket()) {
throw NormalError("Server cannot read from server socket");
}
size_t received = 0;
while (received < length) {
int n = ::read(this->m_internals->fileDescriptor(), reinterpret_cast<char*>(data) + received, length - received);
if (n <= 0) {
throw NormalError("Could not read data");
}
received += n;
}
}