/* * 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;