socket.cc
Go to the documentation of this file.
/*
** socket.cc
** Login : Julien Lemoine <speedblue@happycoders.org>
** Started on Sat Mar 1 23:01:09 2003 Julien Lemoine
** $Id: socket.cc,v 1.16 2004/11/24 21:25:36 speedblue Exp $
**
** Copyright (C) 2003,2004 Julien Lemoine
** 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 2 of the License, or
** (at your option) any later version.
**
** This program 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, write to the Free Software
** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <iostream>
#include <fstream>
#include <sys/types.h>
#include "socket.hh"
namespace Network
{
Socket::Socket(SOCKET_KIND kind, SOCKET_VERSION version) :
_kind(kind), _version(version), _state_timeout(0),
_socket(0), _recv_flags(kind), _proto_kind(text), _empty_lines(false),
_buffer(""), _tls(false)
{
_delim.push_back("\0");
#ifdef LIBSOCKET_WIN
WSADATA wsadata;
if (WSAStartup(MAKEWORD(1, 1), &wsadata) != 0)
throw WSAStartupError("WSAStartup failed", HERE);
#endif
#ifndef IPV6_ENABLED
if (version == V6)
throw Ipv6SupportError("lib was not compiled with ipv6 support", HERE);
#endif
}
Socket::Socket(SOCKET_KIND kind, PROTO_KIND pkind, SOCKET_VERSION version) :
_kind(kind), _version(version), _state_timeout(0),
_socket(0), _recv_flags(kind), _proto_kind(pkind), _empty_lines(false),
_buffer(""), _tls(false)
{
_delim.push_back("\0");
#ifdef LIBSOCKET_WIN
WSADATA wsadata;
if (WSAStartup(MAKEWORD(1, 1), &wsadata) != 0)
throw WSAStartupError("WSAStartup failed", HERE);
#endif
#ifndef IPV6_ENABLED
if (version == V6)
throw Ipv6SupportError("lib was not compiled with ipv6 support", HERE);
#endif
}
Socket::~Socket()
{
}
void Socket::enable_tls()
{
#ifdef TLS
int ret;
if (_kind != TCP)
throw TLSError("You need to have a TCP connection", HERE);
if (!connected())
throw NoConnection("You need to have a connection", HERE);
gnutls_transport_set_ptr(_session, (gnutls_transport_ptr)_socket);
ret = gnutls_handshake(_session);
if (ret < 0)
{
close(_socket);
gnutls_deinit(_session);
throw TLSError(gnutls_strerror(ret), HERE);
}
#else
throw TLSSupportError("lib was not compiled with TLS support", HERE);
#endif
}
void Socket::init_tls(GnuTLSKind kind,
unsigned size, const std::string &certfile,
const std::string &keyfile,
const std::string &trustfile,
const std::string &crlfile)
{
#ifdef TLS
static bool init = false;
static gnutls_dh_params dh_params;
const int protocol_tls[] = { GNUTLS_TLS1, 0 };
const int protocol_ssl[] = { GNUTLS_SSL3, 0 };
const int cert_type_priority[] = { GNUTLS_CRT_X509,
GNUTLS_CRT_OPENPGP, 0 };
if (!init)
{
gnutls_global_init();
init = true;
}
_tls = true;
_tls_main = true;
gnutls_certificate_allocate_credentials(&_x509_cred);
if (keyfile.size() > 0 && certfile.size() > 0)
{
std::ifstream key(keyfile.c_str()), cert(certfile.c_str());
if (!key.is_open() || !cert.is_open())
throw InvalidFile("key or cert invalid", HERE);
key.close();
cert.close();
// Only for server...
_nbbits = size;
if (trustfile.size() > 0)
gnutls_certificate_set_x509_trust_file(_x509_cred, trustfile.c_str(),
GNUTLS_X509_FMT_PEM);
if (crlfile.size() > 0)
gnutls_certificate_set_x509_crl_file(_x509_cred, crlfile.c_str(),
GNUTLS_X509_FMT_PEM);
gnutls_certificate_set_x509_key_file(_x509_cred, certfile.c_str(),
keyfile.c_str(),
GNUTLS_X509_FMT_PEM);
gnutls_dh_params_init(&dh_params);
gnutls_dh_params_generate2(dh_params, _nbbits);
gnutls_certificate_set_dh_params(_x509_cred, dh_params);
if (gnutls_init(&_session, GNUTLS_SERVER))
throw TLSError("gnutls_init failed", HERE);
}
else
{
if (gnutls_init(&_session, GNUTLS_CLIENT))
throw TLSError("gnutls_init failed", HERE);
}
gnutls_set_default_priority(_session);
if (kind == TLS)
gnutls_protocol_set_priority(_session, protocol_tls);
else
gnutls_protocol_set_priority(_session, protocol_ssl);
if (keyfile.size() > 0 && certfile.size() > 0)
{
gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE, _x509_cred);
gnutls_certificate_server_set_request(_session, GNUTLS_CERT_REQUEST);
gnutls_dh_set_prime_bits(_session, _nbbits);
}
else
{
gnutls_certificate_type_set_priority(_session, cert_type_priority);
gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE, _x509_cred);
}
#else
throw TLSSupportError("lib was not compiled with TLS support", HERE);
#endif
}
void Socket::_close(int socket) const
{
#ifndef LIBSOCKET_WIN
if (socket < 0 || close(socket) < 0)
throw CloseError("Close Error", HERE);
socket = 0;
#else
if (socket < 0 || closesocket(socket) < 0)
throw CloseError("Close Error", HERE);
socket = 0;
#endif
#ifdef TLS
if (_tls)
{
std::cout << "Deletion..." << std::endl;
gnutls_deinit(_session);
if (_tls_main)
{
gnutls_certificate_free_credentials(_x509_cred);
gnutls_global_deinit();
}
}
#endif
}
void Socket::_listen(int socket) const
{
if (socket < 0 || listen(socket, 5) < 0)
throw ListenError("Listen Error", HERE);
}
void Socket::_write_str(int socket, const std::string& str) const
{
int res = 1;
unsigned int count = 0;
const char *buf;
buf = str.c_str();
if (socket < 0)
throw NoConnection("No Socket", HERE);
while (res && count < str.size())
{
#ifdef IPV6_ENABLED
if (V4 == _version)
#endif
#ifdef TLS
if (_tls)
res = gnutls_record_send(_session, buf + count, str.size() - count);
else
#endif
res = sendto(socket, buf + count, str.size() - count, SENDTO_FLAGS,
(const struct sockaddr*)&_addr, sizeof(_addr));
#ifdef IPV6_ENABLED
else
res = sendto(socket, buf + count, str.size() - count, SENDTO_FLAGS,
(const struct sockaddr*)&_addr6, sizeof(_addr6));
#endif
if (res <= 0)
throw ConnectionClosed("Connection Closed", HERE);
count += res;
}
}
void Socket::_write_str_bin(int socket, const std::string& str) const
{
int res = 1;
unsigned int count = 0;
#ifdef LIBSOCKET_WIN
char* buf = new char[str.size() + 2];
#else
char buf[str.size() + 2];
#endif
buf[0] = str.size() / 256;
buf[1] = str.size() % 256;
memcpy(buf + 2, str.c_str(), str.size());
if (socket < 0)
throw NoConnection("No Socket", HERE);
while (res && count < str.size() + 2)
{
#ifdef IPV6_ENABLED
if (V4 == _version)
#endif
#ifdef TLS
if (_tls)
res = gnutls_record_send(_session, buf + count, str.size() + 2 - count);
else
#endif
res = sendto(socket, buf + count, str.size() + 2 - count,
SENDTO_FLAGS,
(const struct sockaddr*)&_addr, sizeof(_addr));
#ifdef IPV6_ENABLED
else
res = sendto(socket, buf + count, str.size() + 2 - count,
\ SENDTO_FLAGS,
(const struct sockaddr*)&_addr6, sizeof(_addr6));
#endif
if (res <= 0)
throw ConnectionClosed("Connection Closed", HERE);
count += res;
}
#ifdef LIBSOCKET_WIN
delete[] buf;
#endif
}
void Socket::_set_timeout(bool enable, int socket, int timeout)
{
fd_set fdset;
struct timeval timetowait;
int res;
if (enable)
timetowait.tv_sec = timeout;
else
timetowait.tv_sec = 65535;
timetowait.tv_usec = 0;
FD_ZERO(&fdset);
FD_SET(socket, &fdset);
if (enable)
res = select(socket + 1, &fdset, NULL, NULL, &timetowait);
else
res = select(socket + 1, &fdset, NULL, NULL, NULL);
if (res < 0)
throw SelectError("Select error", HERE);
if (res == 0)
throw Timeout("Timeout on socket", HERE);
}
void Socket::write(const std::string& str)
{
if (_proto_kind == binary)
_write_str_bin(_socket, str);
else
_write_str(_socket, str);
}
bool Socket::connected() const
{
return _socket != 0;
}
void Socket::allow_empty_lines()
{
_empty_lines = true;
}
int Socket::get_socket()
{
return _socket;
}
void Socket::add_delim(const std::string& delim)
{
_delim.push_back(delim);
}
void Socket::del_delim(const std::string& delim)
{
std::list<std::string>::iterator it, it2;
for (it = _delim.begin(); it != _delim.end(); )
{
if (*it == delim)
{
it2 = it++;
_delim.erase(it2);
}
else
it++;
}
}
std::pair<int, int> Socket::_find_delim(const std::string& str, int start) const
{
int i = -1;
int pos = -1, size = 0;
std::list<std::string>::const_iterator it;
// Looking for the first delimiter.
if (_delim.size() > 0)
{
it = _delim.begin();
while (it != _delim.end())
{
if (*it == "")
i = str.find('\0', start);
else
i = str.find(*it, start);
if ((i >= 0) && ((unsigned int)i < str.size()) &&
(pos < 0 || i < pos))
{
pos = i;
size = it->size() ? it->size() : 1;
}
it++;
}
}
return std::pair<int, int>(pos, size);
}
Socket& operator<<(Socket& s, const std::string& str)
{
s.write(str);
return s;
}
Socket& operator>>(Socket& s, std::string& str)
{
str = s.read();
return s;
}
}
Network::Socket
This class represent an abstract socket connection (udp | tcp server | tcp client)
Definition socket.hh:101
Network::Socket::_find_delim
std::pair< int, int > _find_delim(const std::string &str, int start) const
Definition socket.cc:339
Network::Socket::get_socket
int get_socket()
get socket (fd) warning: be very carefull with this method
Definition socket.cc:313
Network::Socket::_tls
bool _tls
Definition socket.hh:203
Network::Socket::Socket
Socket(SOCKET_KIND kind, SOCKET_VERSION version=V4)
Definition socket.cc:31
Network::Socket::_empty_lines
bool _empty_lines
Definition socket.hh:201
Network::Socket::write
void write(const std::string &str)
function used by << operator (write a string on current socket)
Definition socket.cc:295
Network::Socket::_write_str_bin
void _write_str_bin(int socket, const std::string &str) const
Write a string to a socket (when used with binary protocol)
Definition socket.cc:230
Network::Socket::_set_timeout
void _set_timeout(bool enable, int socket, int timeout)
set a timeout on a socket
Definition socket.cc:272
Network::Socket::add_delim
void add_delim(const std::string &delim)
set the delimitor for the text mode
Definition socket.cc:318
Network::Socket::_addr
struct sockaddr_in _addr
Definition socket.hh:195
Network::Socket::del_delim
void del_delim(const std::string &delim)
delete this delimitor for the socket
Definition socket.cc:323
Network::Socket::_kind
SOCKET_KIND _kind
Definition socket.hh:190
Network::Socket::enable_tls
void enable_tls()
Enable TLS on socket.
Definition socket.cc:69
Network::Socket::_socket
int _socket
Definition socket.hh:193
Network::Socket::_version
SOCKET_VERSION _version
Definition socket.hh:191
Network::Socket::_listen
void _listen(int socket) const
Listen on port.
Definition socket.cc:192
Network::Socket::connected
bool connected() const
return true when socket is connected
Definition socket.cc:303
Network::Socket::_close
void _close(int socket) const
Close a connnection.
Definition socket.cc:167
Network::Socket::allow_empty_lines
void allow_empty_lines()
, if set, empty lines will be returned in text procols (if not, they are skipped)
Definition socket.cc:308
Network::Socket::_delim
std::list< std::string > _delim
Definition socket.hh:200
Network::Socket::init_tls
void init_tls(GnuTLSKind kind, unsigned size=1024, const std::string &certfile="", const std::string &keyfile="", const std::string &trustfile="", const std::string &crlfile="")
Definition socket.cc:92
Network::Socket::_write_str
void _write_str(int socket, const std::string &str) const
Write a string to a socket (when used with textual protocol)
Definition socket.cc:198
Network::Socket::read
virtual std::string read()=0
function used by >> operator (read a string on current socket)
Network::Socket::~Socket
virtual ~Socket()
Definition socket.cc:65
Network::Socket::_proto_kind
PROTO_KIND _proto_kind
Definition socket.hh:199
Network
Network namespace represent all networks connection.
Definition localsocket.cc:33
Network::operator>>
Socket & operator>>(Socket &s, std::string &str)
read a string on current socket
Definition socket.cc:373
Network::V4
@ V4
Definition socket.hh:87
Network::V6
@ V6
Definition socket.hh:88
Network::TCP
@ TCP
Definition socket.hh:80
Network::operator<<
Socket & operator<<(Socket &s, const std::string &str)
write a string on current socket
Definition socket.cc:367
Network::text
@ text
Definition socket.hh:74
Network::binary
@ binary
Definition socket.hh:75
Network::SOCKET_VERSION
enum Network::e_version SOCKET_VERSION
Network::SOCKET_KIND
enum Network::e_kind SOCKET_KIND
Network::GnuTLSKind
enum Network::e_gnutls_kind GnuTLSKind
Network::PROTO_KIND
enum Network::e_pkind PROTO_KIND
socket.hh
SENDTO_FLAGS
#define SENDTO_FLAGS
Definition socket.hh:48
HERE
#define HERE
Definition socketexception.hh:26