/* * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * Copyright 2017 Danny Robson */ #include "socket.hpp" #include "platform.hpp" #include "except.hpp" #if !defined(PLATFORM_WIN32) #include #include #include #include #include #else #include #include #endif using cruft::posix::socket; /////////////////////////////////////////////////////////////////////////////// class lookup { public: lookup (const cruft::view host, int port): m_addresses (nullptr, freeaddrinfo) { struct ::addrinfo hints {}; hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; const struct { std::string host; std::string port; } strings { .host = std::string (std::begin (host), std::end (host)), .port = std::to_string (port) }; addrinfo* _addresses; cruft::posix::eai::try_code ( getaddrinfo ( strings.host.c_str (), strings.port.c_str (), &hints, &_addresses ) ); m_addresses.reset (_addresses); }; // TODO: use a wrapper that supports the increment operator auto begin (void) const { return m_addresses.get (); } auto end (void) const { return nullptr; } private: std::unique_ptr m_addresses; }; /////////////////////////////////////////////////////////////////////////////// static cruft::posix::fd connect_host (const cruft::view host, int port) { lookup l {host,port}; for (auto _info = l.begin (); _info; _info = _info->ai_next) { auto &info = *_info; cruft::posix::fd sock { socket (info.ai_family, info.ai_socktype, info.ai_protocol) }; if (sock == -1) continue; if (connect (sock, info.ai_addr, info.ai_addrlen)) continue; return sock; } throw std::runtime_error ("unable to connect"); } /////////////////////////////////////////////////////////////////////////////// socket::socket (int _fd): fd (_fd) { ; } //----------------------------------------------------------------------------- socket::socket (int domain, int type): socket (domain, type, 0) { ; } //----------------------------------------------------------------------------- socket::socket (int domain, int type, int protocol): fd (::socket (domain, type, protocol)) { ; } //----------------------------------------------------------------------------- socket::socket (cruft::view host, int port): fd (connect_host (host, port)) { ; } //----------------------------------------------------------------------------- socket::~socket () { ; } //----------------------------------------------------------------------------- socket::socket (socket &&rhs) noexcept: fd (std::move (rhs)) { ; } //----------------------------------------------------------------------------- class socket& socket::operator= (socket &&rhs) noexcept { fd::operator= (std::move (rhs)); return *this; } /////////////////////////////////////////////////////////////////////////////// void socket::bind (sockaddr const &addr) { auto const size = addr.sa_family == AF_INET ? sizeof (sockaddr_in ) : addr.sa_family == AF_INET6 ? sizeof (sockaddr_in6) : throw std::runtime_error ("Unhandled sockaddr type"); cruft::posix::error::try_call ( ::bind, native (), &addr, size ); } //----------------------------------------------------------------------------- void socket::bind (sockaddr_in const &addr) { return bind (reinterpret_cast (addr)); } /////////////////////////////////////////////////////////////////////////////// void socket::connect (sockaddr_in const &addr) { cruft::posix::error::try_code ( ::connect (native (), reinterpret_cast(&addr), sizeof (addr)) ); } //----------------------------------------------------------------------------- void socket::connect (cruft::view host, int port) { const lookup l { host, port }; for (auto cursor = l.begin (); cursor != l.end (); cursor = cursor->ai_next) { if (!::connect (*this, cursor->ai_addr, cursor->ai_addrlen)) return; } throw std::runtime_error ("unable to reconnect"); } /////////////////////////////////////////////////////////////////////////////// template ssize_t socket::sendto (cruft::view buffer, int flags, ::sockaddr_in const &dst) { return cruft::posix::error::try_value ( ::sendto ( native (), buffer.data (), buffer.size (), flags, reinterpret_cast (&dst), sizeof (dst) ) ); } template ssize_t socket::sendto (cruft::view, int, sockaddr_in const&); template ssize_t socket::sendto (cruft::view, int, sockaddr_in const&); template ssize_t socket::sendto (cruft::view, int, sockaddr_in const&); //----------------------------------------------------------------------------- template cruft::view socket::recvfrom (cruft::view buffer, int flags) { auto res = cruft::posix::error::try_value ( ::recvfrom (native (), buffer.data (), buffer.size (), flags, nullptr, nullptr) ); return { buffer.begin (), res }; } template cruft::view socket::recvfrom (cruft::view buffer, int flags); template cruft::view socket::recvfrom (cruft::view buffer, int flags); template cruft::view socket::recvfrom (cruft::view buffer, int flags);