libscio/socketstream.cpp

190 lines
5.5 KiB
C++

//
// socketstream.cpp
// libscio
//
// Created by Bob Polis at 2020-03-02
// Copyright (c) 2020 SwiftCoder. All rights reserved.
//
#include "libscio.hpp"
#include <libscerror.hpp>
#include <unistd.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/un.h>
#include <cstring>
#include <vector>
#include <utility>
using namespace sc::io;
socketstream::socketstream(int domain, int type)
: std::iostream(&_iobuf), _domain(domain), _type(type) {
_iobuf.fd(::socket(_domain, _type, 0));
throw_if_min1_msg(_iobuf.fd(), "could not create socket");
std::memset(&_sa, 0, sizeof(struct sockaddr_storage));
}
socketstream::socketstream() : std::iostream(&_iobuf) {
std::memset(&_sa, 0, sizeof(struct sockaddr_storage));
}
socketstream::socketstream(socketstream&& other) : std::iostream(&_iobuf) {
_iobuf.fd(other._iobuf.fd());
_domain = other._domain;
_type = other._type;
std::memcpy(&_sa, &other._sa, sizeof(struct sockaddr_storage));
other._iobuf.fd(-1);
std::memset(&other._sa, 0, sizeof(struct sockaddr_storage));
}
socketstream& socketstream::operator=(socketstream&& other) {
if (this != &other) {
_iobuf.fd(other._iobuf.fd());
_domain = other._domain;
_type = other._type;
std::memcpy(&_sa, &other._sa, sizeof(struct sockaddr_storage));
other._iobuf.fd(-1);
std::memset(&other._sa, 0, sizeof(struct sockaddr_storage));
}
return *this;
}
socketstream::~socketstream() {
if (_iobuf.fd() != -1) {
::close(_iobuf.fd());
}
}
void socketstream::make_server(const socket_address& sa) {
size_t addr_sz = 0;
switch (_domain) {
case AF_UNIX: {
struct sockaddr_un* sau = reinterpret_cast<struct sockaddr_un*>(&_sa);
sau->sun_family = AF_UNIX;
addr_sz = sa.address.size();
std::strncpy(sau->sun_path, sa.address.c_str(), addr_sz);
break;
}
case AF_INET: {
struct sockaddr_in* sa4 = reinterpret_cast<struct sockaddr_in*>(&_sa);
sa4->sin_family = AF_INET;
sa4->sin_addr.s_addr = INADDR_ANY;
sa4->sin_port = htons(sa.port);
addr_sz = sizeof(struct sockaddr_in);
break;
}
case AF_INET6: {
struct sockaddr_in6* sa6 = reinterpret_cast<struct sockaddr_in6*>(&_sa);
sa6->sin6_family = AF_INET6;
sa6->sin6_addr = in6addr_any;
sa6->sin6_port = htons(sa.port);
addr_sz = sizeof(struct sockaddr_in6);
break;
}
default:
errno = EAFNOSUPPORT;
throw_if_min1_msg(-1, "unsupported socket domain");
}
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&_sa);
throw_if_min1(::bind(_iobuf.fd(), addr, addr_sz));
if (_type == SOCK_STREAM) {
throw_if_min1(::listen(_iobuf.fd(), SOMAXCONN));
}
}
socketstream socketstream::accept() const {
socketstream peer;
socklen_t len = sizeof(struct sockaddr_storage);
int active_socket = ::accept(_iobuf.fd(), reinterpret_cast<struct sockaddr*>(&peer._sa), &len);
throw_if_min1(active_socket);
peer._iobuf.fd(active_socket);
peer._domain = _domain;
peer._type = _type;
peer._iobuf.fd(active_socket);
return peer;
}
void socketstream::connect(const socket_address& host) {
endpoint_from_address(host, &_sa);
struct sockaddr* addr = reinterpret_cast<struct sockaddr*>(&_sa);
throw_if_min1(::connect(_iobuf.fd(), addr, sizeof(_sa)));
}
std::string socketstream::address() const {
const struct sockaddr* addr = reinterpret_cast<const struct sockaddr*>(&_sa);
socket_address sa = address_from_endpoint(addr);
return sa.address;
}
void socketstream::endpoint_from_address(const socket_address& address,
struct sockaddr_storage* endpoint) const {
throw_if_null(endpoint);
switch (_domain) {
case AF_UNIX: {
struct sockaddr_un* addr = reinterpret_cast<struct sockaddr_un*>(endpoint);
addr->sun_family = AF_UNIX;
std::memcpy(addr->sun_path, address.address.c_str(), address.address.size());
break;
}
case AF_INET: {
struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(endpoint);
addr->sin_family = AF_INET;
addr->sin_port = htons(address.port);
::inet_pton(_domain, address.address.c_str(), &addr->sin_addr);
break;
}
case AF_INET6: {
struct sockaddr_in6* addr = reinterpret_cast<struct sockaddr_in6*>(endpoint);
addr->sin6_family = AF_INET6;
addr->sin6_port = htons(address.port);
addr->sin6_flowinfo = 0;
::inet_pton(_domain, address.address.c_str(), &addr->sin6_addr);
addr->sin6_scope_id = 0;
break;
}
default:
errno = EAFNOSUPPORT;
throw_if_min1_msg(-1, "unsupported socket domain");
}
}
socket_address socketstream::address_from_endpoint(const struct sockaddr* endpoint) const {
throw_if_null(endpoint);
socket_address sa;
switch (_domain) {
case AF_UNIX: {
const struct sockaddr_un* addr = reinterpret_cast<const struct sockaddr_un*>(endpoint);
sa.address = addr->sun_path;
sa.port = 0;
break;
}
case AF_INET: {
const struct sockaddr_in* addr = reinterpret_cast<const struct sockaddr_in*>(endpoint);
std::vector<char> buf;
buf.resize(INET_ADDRSTRLEN);
::inet_ntop(_domain, &addr->sin_addr, buf.data(), buf.size());
std::string temp {buf.data(), buf.size()};
sa.address = std::move(temp);
sa.port = ntohs(addr->sin_port);
break;
}
case AF_INET6: {
const struct sockaddr_in6* addr = reinterpret_cast<const struct sockaddr_in6*>(endpoint);
std::vector<char> buf;
buf.resize(INET6_ADDRSTRLEN);
::inet_ntop(_domain, &addr->sin6_addr, buf.data(), buf.size());
std::string temp {buf.data(), buf.size()};
sa.address = std::move(temp);
sa.port = ntohs(addr->sin6_port);
break;
}
default:
errno = EAFNOSUPPORT;
throw_if_min1_msg(-1, "unsupported socket domain");
}
return sa;
}