/**
\file socket.cpp
\author Seung Geol Choi
\copyright ABY - A Framework for Efficient Mixed-protocol Secure Two-party Computation
Copyright (C) 2019 ENCRYPTO Group, TU Darmstadt
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
ABY is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program. If not, see .
\brief Socket Implementation
*/
#include "socket.h"
#include "utils.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using boost::asio::ip::tcp;
struct CSocket::CSocketImpl {
CSocketImpl(std::shared_ptr io_context,
tcp::socket&& socket)
: io_context(io_context), socket(std::move(socket)),
acceptor(*io_context)
{
}
CSocketImpl()
: io_context(std::make_shared()),
socket(*io_context), acceptor(*io_context)
{}
std::shared_ptr io_context;
tcp::socket socket;
tcp::acceptor acceptor;
};
CSocket::CSocket(bool verbose)
: impl_(std::make_unique()), send_count_(0), recv_count_(0),
verbose_(verbose)
{}
CSocket::~CSocket() {
Close();
}
uint64_t CSocket::getSndCnt() const {
std::lock_guard lock(send_count_mutex_);
return send_count_;
}
uint64_t CSocket::getRcvCnt() const {
std::lock_guard lock(recv_count_mutex_);
return recv_count_;
}
void CSocket::ResetSndCnt() {
std::lock_guard lock(send_count_mutex_);
send_count_ = 0;
}
void CSocket::ResetRcvCnt() {
std::lock_guard lock(recv_count_mutex_);
recv_count_ = 0;
}
bool CSocket::Socket() {
return true;
}
void CSocket::Close() {
impl_->socket.close();
}
std::string CSocket::GetIP() const {
boost::system::error_code ec;
auto endpoint = impl_->socket.local_endpoint(ec);
if (ec) {
return "";
}
return endpoint.address().to_string();
}
uint16_t CSocket::GetPort() const {
boost::system::error_code ec;
auto endpoint = impl_->socket.local_endpoint(ec);
if (ec) {
return 0;
}
return endpoint.port();
}
bool CSocket::Bind(const std::string& ip, uint16_t port) {
boost::system::error_code ec;
boost::asio::ip::address address;
if (ip.empty()) {
// Use "::" if no address is given
address = boost::asio::ip::address_v6();
} else {
// Try to parse given address
address = boost::asio::ip::make_address(ip, ec);
if (ec) {
if (verbose_) {
std::cerr << "make_address failed: " << ec.message() << "\n";
std::cerr << "with argument: " << ip << "\n";
}
return false;
}
}
tcp::endpoint endpoint(address, port);
impl_->acceptor.open(endpoint.protocol(), ec);
if (ec) {
if (verbose_) {
std::cerr << "acceptor socket open failed: " << ec.message() << "\n";
std::cerr << "endpoint: " << endpoint << "\n";
}
return false;
}
// Use dual stack IPv4 and IPv6
if (endpoint.protocol() == tcp::v6()) {
boost::asio::ip::v6_only opt(false);
impl_->acceptor.set_option(opt, ec);
if (ec) {
if (verbose_) {
std::cerr << "acceptor disable option IPPROTO_IPV6/IP_V6ONLY failed: "
<< ec.message() << "\n";
}
return false;
}
}
// Set socket options
boost::asio::socket_base::reuse_address opt_reuse_addr(true);
tcp::no_delay opt_tcp_no_delay(true);
impl_->acceptor.set_option(opt_reuse_addr, ec);
if (ec) {
if (verbose_) {
std::cerr << "acceptor set option SO_REUSEADDR failed: " << ec.message() << "\n";
std::cerr << "endpoint: " << endpoint << "\n";
}
return false;
}
impl_->acceptor.set_option(opt_tcp_no_delay, ec);
if (ec) {
if (verbose_) {
std::cerr << "acceptor set option TCP_NODELAY failed: " << ec.message() << "\n";
std::cerr << "endpoint: " << endpoint << "\n";
}
return false;
}
impl_->acceptor.bind(endpoint, ec);
if (ec) {
if (verbose_) {
std::cerr << "bind failed: " << ec.message() << "\n";
std::cerr << "endpoint: " << endpoint << "\n";
}
return false;
}
return true;
}
bool CSocket::Listen(int backlog) {
boost::system::error_code ec;
impl_->acceptor.listen(backlog);
if (ec) {
if (verbose_) {
std::cerr << "listen failed: " << ec.message() << "\n";
std::cerr << "endpoint: " << impl_->acceptor.local_endpoint() << "\n";
}
return false;
}
return true;
}
std::unique_ptr CSocket::Accept() {
boost::system::error_code ec;
auto socket = impl_->acceptor.accept(ec);
if (ec) {
if (verbose_) {
std::cerr << "accept failed: " << ec.message() << "\n";
std::cerr << "endpoint: " << impl_->acceptor.local_endpoint() << "\n";
}
return nullptr;
}
auto csocket = std::make_unique();
csocket->impl_ = std::make_unique(impl_->io_context, std::move(socket));
return csocket;
}
bool CSocket::Connect(const std::string& host, uint16_t port) {
boost::system::error_code ec;
tcp::resolver resolver(*impl_->io_context);
auto endpoints = resolver.resolve(host, std::to_string(port), ec);
if (ec) {
if (verbose_) {
std::cerr << "resolve failed: " << ec.message() << "\n";
}
return false;
}
boost::asio::connect(impl_->socket, endpoints, ec);
if (ec) {
if (verbose_) {
std::cerr << "connect failed: " << ec.message() << "\n";
}
return false;
}
tcp::no_delay opt_tcp_no_delay(true);
impl_->socket.set_option(opt_tcp_no_delay, ec);
if (ec) {
if (verbose_) {
std::cerr << "socket set option TCP_NODELAY failed: " << ec.message() << "\n";
}
return false;
}
return true;
}
size_t CSocket::Receive(void* buf, size_t bytes) {
boost::system::error_code ec;
auto bytes_transferred =
boost::asio::read(impl_->socket, boost::asio::buffer(buf, bytes), ec);
if (ec && verbose_) {
std::cerr << "read failed: " << ec.message() << "\n";
}
{
std::lock_guard lock(recv_count_mutex_);
recv_count_ += bytes_transferred;
}
return bytes_transferred;
}
size_t CSocket::Send(const void* buf, size_t bytes) {
boost::system::error_code ec;
auto bytes_transferred =
boost::asio::write(impl_->socket, boost::asio::buffer(buf, bytes), ec);
if (ec && verbose_) {
std::cerr << "write failed: " << ec.message() << "\n";
}
{
std::lock_guard lock(send_count_mutex_);
send_count_ += bytes_transferred;
}
return bytes_transferred;
}