/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim 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 General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see .
*
* Copyright 2011 Danny Robson
*/
#include "socket.hpp"
#include "../debug.hpp"
#include "../except.hpp"
#include "../types/casts.hpp"
#include "../log.hpp"
#include "except.hpp"
#if defined(HAVE_WINSOCK2_H)
#include
#else
#include
#endif
#include
#include
using namespace net;
//-----------------------------------------------------------------------------
template
socket_domain::socket_domain (socket_t _fd):
m_fd (_fd)
{
#ifdef __WIN32
#else
CHECK_GE (m_fd, 0);
#endif
}
#if defined(HAVE_WINSOCK2_H)
// TODO: Make this not retarded. Fucking Windows.
#define dup(X) (X)
// Winsock has incorrect return and parameter types (according to POSIX)
// so we need some wrappers around the system implementations that casts
// to expected types. Mainly int vs ssize_t returns, and int vs size_t
// parameters.
static_assert(sizeof(int) <= sizeof(ssize_t), "int != ssize_t");
static_assert(sizeof(int) <= sizeof( size_t), "int != size_t");
ssize_t recv(socket_t _socket, void *_buf, size_t _len, int _flags)
{ return (ssize_t)::recv(_socket, (char*)_buf, (int)_len, _flags); }
ssize_t recvfrom(socket_t _socket, void *_buf, size_t _len, int _flags, struct sockaddr *_sockaddr, socklen_t *_socklen)
{ return (ssize_t)::recvfrom(_socket, (char*)_buf, (int)_len, _flags, _sockaddr, (int*)_socklen); }
ssize_t sendto(socket_t _socket, const void *_buf, size_t _len, int _flags, const struct sockaddr *_sockaddr, socklen_t _socklen)
{ return (ssize_t)::sendto(_socket, (const char*)_buf, (int)_len, _flags, _sockaddr, (int)_socklen); }
ssize_t send(socket_t _socket, const void *_buf, size_t _len, int _flags)
{ return (ssize_t)::send(_socket, (const char*)_buf, (int)_len, _flags); }
#else
#define closesocket(X) close(X)
#endif
template
socket_domain::socket_domain (const socket_domain &rhs):
m_fd (dup (rhs.m_fd))
{ ; }
template
socket_domain::~socket_domain () {
if (closesocket (m_fd) < 0) {
LOG_DEBUG ("closesocket: %s", strerror (errno));
}
}
template
socket_t
socket_domain::native (void) const
{ return m_fd; }
template
void
socket_domain::shutdown (void) {
#if defined(HAVE_WINSOCK2_H)
#define SHUT_SEND SD_SEND
#define SHUT_RECV SD_RECEIVE
#define SHUT_RDWR SD_BOTH
#endif
if (::shutdown (m_fd, SHUT_RDWR) < 0)
net::error::throw_code ();
}
template
template
T
socket_domain::get_option (level, option) {
not_implemented ();
return T ();
}
template
void
socket_domain::set_option (level _level, option _option) {
if (setsockopt (this->m_fd, (int)_level, (int)_option, NULL, 0))
net::error::throw_code ();
}
template
template
void
socket_domain::set_option (level _level, option _option, const T &value) {
if (setsockopt (this->m_fd, (int)_level, (int)_option, (const char*)&value, sizeof (value)))
net::error::throw_code ();
}
template
void
socket_domain::bind (const address_type &addr) {
typename address_type::sockaddr_type addr_in = addr.to_sockaddr ();
if (::bind (m_fd, (sockaddr *)&addr_in, sizeof (addr_in)) != 0)
net::error::throw_code ();
}
//-----------------------------------------------------------------------------
template
net::socket::socket (socket_t _fd):
net::socket_domain (_fd)
{ ; }
template
net::socket::socket ():
net::socket_domain (::socket ((int)D, (int)type::STREAM, (int)protocol::DEFAULT))
{ ; }
template
net::socket::socket (const socket_type &rhs):
socket_domain (rhs)
{ ; }
template
void
net::socket::send (const uint8_t *restrict data, size_t len) {
CHECK (data != NULL);
CHECK_GT (len, 0);
for (size_t sent = 0; sent < len; ) {
ssize_t result = ::send (this->m_fd, static_cast(data + sent), len - sent, 0);
if (result < 0)
net::error::throw_code ();
sent += sign_cast (result);
}
}
template
size_t
net::socket::recv (uint8_t *restrict data, size_t len) {
CHECK (data != NULL);
CHECK_GT (len, 0);
ssize_t received = ::recv (this->m_fd, data, len, 0);
if (received < 0)
net::error::throw_code ();
return sign_cast (received);
}
template
void
net::socket::connect (const address_type &_addr) {
typename address_type::sockaddr_type addr (_addr.to_sockaddr ());
if (::connect (this->m_fd, reinterpret_cast (&addr), sizeof (addr)) < 0)
net::error::throw_code ();
}
template
void
net::socket::listen (const address_type &_addr, unsigned int _backlog) {
this->bind (_addr);
if (::listen (this->m_fd, sign_cast(_backlog)) != 0)
net::error::throw_code ();
}
template
typename net::socket::socket_ptr
net::socket::accept (void) {
socket_t newfd = ::accept (this->m_fd, NULL, 0);
if (newfd == INVALID_SOCKET)
net::error::throw_code ();
return socket_ptr(new socket (newfd));
}
template
typename net::socket::address_type
net::socket::get_peer (void) const {
typename address_type::sockaddr_type addr;
socklen_t addr_len;
if (getpeername (this->m_fd, (sockaddr*)&addr, &addr_len))
net::error::throw_code ();
CHECK (addr_len == sizeof (addr));
return addr;
}
//-----------------------------------------------------------------------------
template
net::socket::socket (socket_t _fd):
net::socket_domain (_fd)
{ ; }
template
net::socket::socket ():
net::socket_domain (::socket ((int)D, (int)type::DGRAM, (int)protocol::DEFAULT))
{ ; }
template
net::socket::socket (const socket_type &rhs):
net::socket_domain (rhs)
{ ; }
template
void
net::socket::send_addr (const address_type &addr,
const uint8_t *restrict data,
size_t len) {
CHECK (data != NULL);
CHECK_GT (len, 0);
typename address_type::sockaddr_type addr_in = addr.to_sockaddr ();
ssize_t sent = ::sendto (this->m_fd, data, len, 0, (sockaddr *)&addr_in, sizeof (addr_in));
if (sent < 0)
net::error::throw_code ();
CHECK_EQ (sign_cast(sent), len);
}
template
typename net::socket::address_type
net::socket::recv_addr (uint8_t *restrict data,
size_t len) {
CHECK (data != NULL);
CHECK_GT (len, 0);
typename address_type::sockaddr_type addr_in;
socklen_t addr_len = sizeof (addr_in);
ssize_t recvd = recvfrom (this->m_fd, data, len, 0, (sockaddr *)&addr_in, &addr_len);
CHECK_EQ (sizeof (addr_in), addr_len);
if (recvd < 0)
net::error::throw_code ();
return addr_in;
}
//-----------------------------------------------------------------------------
template class net::socket_domain;
template void net::socket_domain::set_option(level, option, const int&);
template class net::socket;
template class net::socket;