libsocket  1.5
socket.cc
Go to the documentation of this file.
1 /*
2 ** socket.cc
3 ** Login : Julien Lemoine <speedblue@happycoders.org>
4 ** Started on Sat Mar 1 23:01:09 2003 Julien Lemoine
5 ** $Id: socket.cc,v 1.16 2004/11/24 21:25:36 speedblue Exp $
6 **
7 ** Copyright (C) 2003,2004 Julien Lemoine
8 ** This program is free software; you can redistribute it and/or modify
9 ** it under the terms of the GNU Lesser General Public License as published by
10 ** the Free Software Foundation; either version 2 of the License, or
11 ** (at your option) any later version.
12 **
13 ** This program is distributed in the hope that it will be useful,
14 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
15 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ** GNU Lesser General Public License for more details.
17 **
18 ** You should have received a copy of the GNU Lesser General Public License
19 ** along with this program; if not, write to the Free Software
20 ** Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 */
22 
23 #include <iostream>
24 #include <fstream>
25 #include <sys/types.h>
26 #include "socket.hh"
27 
28 namespace Network
29 {
30 
32  _kind(kind), _version(version), _state_timeout(0),
33  _socket(0), _recv_flags(kind), _proto_kind(text), _empty_lines(false),
34  _buffer(""), _tls(false)
35  {
36  _delim.push_back("\0");
37 #ifdef LIBSOCKET_WIN
38  WSADATA wsadata;
39  if (WSAStartup(MAKEWORD(1, 1), &wsadata) != 0)
40  throw WSAStartupError("WSAStartup failed", HERE);
41 #endif
42 #ifndef IPV6_ENABLED
43  if (version == V6)
44  throw Ipv6SupportError("lib was not compiled with ipv6 support", HERE);
45 #endif
46  }
47 
49  _kind(kind), _version(version), _state_timeout(0),
50  _socket(0), _recv_flags(kind), _proto_kind(pkind), _empty_lines(false),
51  _buffer(""), _tls(false)
52  {
53  _delim.push_back("\0");
54 #ifdef LIBSOCKET_WIN
55  WSADATA wsadata;
56  if (WSAStartup(MAKEWORD(1, 1), &wsadata) != 0)
57  throw WSAStartupError("WSAStartup failed", HERE);
58 #endif
59 #ifndef IPV6_ENABLED
60  if (version == V6)
61  throw Ipv6SupportError("lib was not compiled with ipv6 support", HERE);
62 #endif
63  }
64 
66  {
67  }
68 
70  {
71 #ifdef TLS
72  int ret;
73 
74  if (_kind != TCP)
75  throw TLSError("You need to have a TCP connection", HERE);
76  if (!connected())
77  throw NoConnection("You need to have a connection", HERE);
78 
79  gnutls_transport_set_ptr(_session, (gnutls_transport_ptr)_socket);
80  ret = gnutls_handshake(_session);
81  if (ret < 0)
82  {
83  close(_socket);
84  gnutls_deinit(_session);
85  throw TLSError(gnutls_strerror(ret), HERE);
86  }
87 #else
88  throw TLSSupportError("lib was not compiled with TLS support", HERE);
89 #endif
90  }
91 
93  unsigned size, const std::string &certfile,
94  const std::string &keyfile,
95  const std::string &trustfile,
96  const std::string &crlfile)
97  {
98 #ifdef TLS
99  static bool init = false;
100  static gnutls_dh_params dh_params;
101  const int protocol_tls[] = { GNUTLS_TLS1, 0 };
102  const int protocol_ssl[] = { GNUTLS_SSL3, 0 };
103  const int cert_type_priority[] = { GNUTLS_CRT_X509,
104  GNUTLS_CRT_OPENPGP, 0 };
105 
106  if (!init)
107  {
108  gnutls_global_init();
109  init = true;
110  }
111  _tls = true;
112  _tls_main = true;
113  gnutls_certificate_allocate_credentials(&_x509_cred);
114  if (keyfile.size() > 0 && certfile.size() > 0)
115  {
116  std::ifstream key(keyfile.c_str()), cert(certfile.c_str());
117  if (!key.is_open() || !cert.is_open())
118  throw InvalidFile("key or cert invalid", HERE);
119  key.close();
120  cert.close();
121  // Only for server...
122  _nbbits = size;
123  if (trustfile.size() > 0)
124  gnutls_certificate_set_x509_trust_file(_x509_cred, trustfile.c_str(),
125  GNUTLS_X509_FMT_PEM);
126  if (crlfile.size() > 0)
127  gnutls_certificate_set_x509_crl_file(_x509_cred, crlfile.c_str(),
128  GNUTLS_X509_FMT_PEM);
129  gnutls_certificate_set_x509_key_file(_x509_cred, certfile.c_str(),
130  keyfile.c_str(),
131  GNUTLS_X509_FMT_PEM);
132  gnutls_dh_params_init(&dh_params);
133  gnutls_dh_params_generate2(dh_params, _nbbits);
134  gnutls_certificate_set_dh_params(_x509_cred, dh_params);
135 
136  if (gnutls_init(&_session, GNUTLS_SERVER))
137  throw TLSError("gnutls_init failed", HERE);
138  }
139  else
140  {
141  if (gnutls_init(&_session, GNUTLS_CLIENT))
142  throw TLSError("gnutls_init failed", HERE);
143  }
144 
145  gnutls_set_default_priority(_session);
146  if (kind == TLS)
147  gnutls_protocol_set_priority(_session, protocol_tls);
148  else
149  gnutls_protocol_set_priority(_session, protocol_ssl);
150 
151  if (keyfile.size() > 0 && certfile.size() > 0)
152  {
153  gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE, _x509_cred);
154  gnutls_certificate_server_set_request(_session, GNUTLS_CERT_REQUEST);
155  gnutls_dh_set_prime_bits(_session, _nbbits);
156  }
157  else
158  {
159  gnutls_certificate_type_set_priority(_session, cert_type_priority);
160  gnutls_credentials_set(_session, GNUTLS_CRD_CERTIFICATE, _x509_cred);
161  }
162 #else
163  throw TLSSupportError("lib was not compiled with TLS support", HERE);
164 #endif
165  }
166 
167  void Socket::_close(int socket) const
168  {
169 #ifndef LIBSOCKET_WIN
170  if (socket < 0 || close(socket) < 0)
171  throw CloseError("Close Error", HERE);
172  socket = 0;
173 #else
174  if (socket < 0 || closesocket(socket) < 0)
175  throw CloseError("Close Error", HERE);
176  socket = 0;
177 #endif
178 #ifdef TLS
179  if (_tls)
180  {
181  std::cout << "Deletion..." << std::endl;
182  gnutls_deinit(_session);
183  if (_tls_main)
184  {
185  gnutls_certificate_free_credentials(_x509_cred);
186  gnutls_global_deinit();
187  }
188  }
189 #endif
190  }
191 
192  void Socket::_listen(int socket) const
193  {
194  if (socket < 0 || listen(socket, 5) < 0)
195  throw ListenError("Listen Error", HERE);
196  }
197 
198  void Socket::_write_str(int socket, const std::string& str) const
199  {
200  int res = 1;
201  unsigned int count = 0;
202  const char *buf;
203 
204  buf = str.c_str();
205  if (socket < 0)
206  throw NoConnection("No Socket", HERE);
207  while (res && count < str.size())
208  {
209 #ifdef IPV6_ENABLED
210  if (V4 == _version)
211 #endif
212 #ifdef TLS
213  if (_tls)
214  res = gnutls_record_send(_session, buf + count, str.size() - count);
215  else
216 #endif
217  res = sendto(socket, buf + count, str.size() - count, SENDTO_FLAGS,
218  (const struct sockaddr*)&_addr, sizeof(_addr));
219 #ifdef IPV6_ENABLED
220  else
221  res = sendto(socket, buf + count, str.size() - count, SENDTO_FLAGS,
222  (const struct sockaddr*)&_addr6, sizeof(_addr6));
223 #endif
224  if (res <= 0)
225  throw ConnectionClosed("Connection Closed", HERE);
226  count += res;
227  }
228  }
229 
230  void Socket::_write_str_bin(int socket, const std::string& str) const
231  {
232  int res = 1;
233  unsigned int count = 0;
234 #ifdef LIBSOCKET_WIN
235  char* buf = new char[str.size() + 2];
236 #else
237  char buf[str.size() + 2];
238 #endif
239  buf[0] = str.size() / 256;
240  buf[1] = str.size() % 256;
241  memcpy(buf + 2, str.c_str(), str.size());
242  if (socket < 0)
243  throw NoConnection("No Socket", HERE);
244  while (res && count < str.size() + 2)
245  {
246 #ifdef IPV6_ENABLED
247  if (V4 == _version)
248 #endif
249 #ifdef TLS
250  if (_tls)
251  res = gnutls_record_send(_session, buf + count, str.size() + 2 - count);
252  else
253 #endif
254  res = sendto(socket, buf + count, str.size() + 2 - count,
255  SENDTO_FLAGS,
256  (const struct sockaddr*)&_addr, sizeof(_addr));
257 #ifdef IPV6_ENABLED
258  else
259  res = sendto(socket, buf + count, str.size() + 2 - count,
260  \ SENDTO_FLAGS,
261  (const struct sockaddr*)&_addr6, sizeof(_addr6));
262 #endif
263  if (res <= 0)
264  throw ConnectionClosed("Connection Closed", HERE);
265  count += res;
266  }
267 #ifdef LIBSOCKET_WIN
268  delete[] buf;
269 #endif
270  }
271 
272  void Socket::_set_timeout(bool enable, int socket, int timeout)
273  {
274  fd_set fdset;
275  struct timeval timetowait;
276  int res;
277 
278  if (enable)
279  timetowait.tv_sec = timeout;
280  else
281  timetowait.tv_sec = 65535;
282  timetowait.tv_usec = 0;
283  FD_ZERO(&fdset);
284  FD_SET(socket, &fdset);
285  if (enable)
286  res = select(socket + 1, &fdset, NULL, NULL, &timetowait);
287  else
288  res = select(socket + 1, &fdset, NULL, NULL, NULL);
289  if (res < 0)
290  throw SelectError("Select error", HERE);
291  if (res == 0)
292  throw Timeout("Timeout on socket", HERE);
293  }
294 
295  void Socket::write(const std::string& str)
296  {
297  if (_proto_kind == binary)
298  _write_str_bin(_socket, str);
299  else
300  _write_str(_socket, str);
301  }
302 
303  bool Socket::connected() const
304  {
305  return _socket != 0;
306  }
307 
309  {
310  _empty_lines = true;
311  }
312 
314  {
315  return _socket;
316  }
317 
318  void Socket::add_delim(const std::string& delim)
319  {
320  _delim.push_back(delim);
321  }
322 
323  void Socket::del_delim(const std::string& delim)
324  {
325  std::list<std::string>::iterator it, it2;
326 
327  for (it = _delim.begin(); it != _delim.end(); )
328  {
329  if (*it == delim)
330  {
331  it2 = it++;
332  _delim.erase(it2);
333  }
334  else
335  it++;
336  }
337  }
338 
339  std::pair<int, int> Socket::_find_delim(const std::string& str, int start) const
340  {
341  int i = -1;
342  int pos = -1, size = 0;
343  std::list<std::string>::const_iterator it;
344 
345  // Looking for the first delimiter.
346  if (_delim.size() > 0)
347  {
348  it = _delim.begin();
349  while (it != _delim.end())
350  {
351  if (*it == "")
352  i = str.find('\0', start);
353  else
354  i = str.find(*it, start);
355  if ((i >= 0) && ((unsigned int)i < str.size()) &&
356  (pos < 0 || i < pos))
357  {
358  pos = i;
359  size = it->size() ? it->size() : 1;
360  }
361  it++;
362  }
363  }
364  return std::pair<int, int>(pos, size);
365  }
366 
367  Socket& operator<<(Socket& s, const std::string& str)
368  {
369  s.write(str);
370  return s;
371  }
372 
373  Socket& operator>>(Socket& s, std::string& str)
374  {
375  str = s.read();
376  return s;
377  }
378 }
This class represent an abstract socket connection (udp | tcp server | tcp client) ...
Definition: socket.hh:100
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
int get_socket()
get socket (fd) warning: be very carefull with this method
Definition: socket.cc:313
Socket & operator<<(Socket &s, const std::string &str)
write a string on current socket
Definition: socket.cc:367
void enable_tls()
Enable TLS on socket.
Definition: socket.cc:69
Network namespace represent all networks connection.
Definition: localsocket.cc:32
void _close(int socket) const
Close a connnection.
Definition: socket.cc:167
struct sockaddr_in _addr
Definition: socket.hh:195
enum Network::e_gnutls_kind GnuTLSKind
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
void _set_timeout(bool enable, int socket, int timeout)
set a timeout on a socket
Definition: socket.cc:272
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
Socket(SOCKET_KIND kind, SOCKET_VERSION version=V4)
Definition: socket.cc:31
#define SENDTO_FLAGS
Definition: socket.hh:48
std::string _buffer
Definition: socket.hh:202
bool connected() const
return true when socket is connected
Definition: socket.cc:303
PROTO_KIND _proto_kind
Definition: socket.hh:199
unsigned _state_timeout
Definition: socket.hh:192
std::list< std::string > _delim
Definition: socket.hh:200
bool _empty_lines
Definition: socket.hh:201
SOCKET_VERSION _version
Definition: socket.hh:191
virtual std::string read()=0
function used by >> operator (read a string on current socket)
void write(const std::string &str)
function used by << operator (write a string on current socket)
Definition: socket.cc:295
SOCKET_KIND _kind
Definition: socket.hh:190
void allow_empty_lines()
, if set, empty lines will be returned in text procols (if not, they are skipped) ...
Definition: socket.cc:308
#define HERE
enum Network::e_version SOCKET_VERSION
enum Network::e_kind SOCKET_KIND
enum Network::e_pkind PROTO_KIND
Socket & operator>>(Socket &s, std::string &str)
read a string on current socket
Definition: socket.cc:373
virtual ~Socket()
Definition: socket.cc:65
void del_delim(const std::string &delim)
delete this delimitor for the socket
Definition: socket.cc:323
void _listen(int socket) const
Listen on port.
Definition: socket.cc:192
void add_delim(const std::string &delim)
set the delimitor for the text mode
Definition: socket.cc:318
std::pair< int, int > _find_delim(const std::string &str, int start) const
Definition: socket.cc:339