The Battle for Wesnoth  1.13.4+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
network.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2016 by David White <[email protected]>
3  Part of the Battle for Wesnoth Project http://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 /**
16  * @file
17  * Networking
18  */
19 
20 #include "global.hpp"
21 
22 #include "gettext.hpp"
23 #include "log.hpp"
24 #include "network_worker.hpp"
26 #include "thread.hpp"
27 #include "util.hpp"
28 #include "config.hpp"
29 
30 #include "filesystem.hpp"
31 
32 
33 #include <cerrno>
34 #include <queue>
35 #include <iomanip>
36 #include <set>
37 #include <cstring>
38 #include <stdexcept>
39 #include <sstream>
40 
41 #include <boost/exception/get_error_info.hpp>
42 #include <boost/exception/info.hpp>
43 
44 #include <signal.h>
45 #if defined(_WIN32) || defined(__WIN32__) || defined (WIN32)
46 #undef INADDR_ANY
47 #undef INADDR_BROADCAST
48 #undef INADDR_NONE
49 #include <windows.h>
50 #else
51 #include <sys/types.h>
52 #include <sys/socket.h>
53 #include <netinet/in.h>
54 #include <netinet/tcp.h> // for TCP_NODELAY
55 #include <fcntl.h>
56 #define SOCKET int
57 #endif
58 
59 #pragma GCC diagnostic ignored "-Wold-style-cast"
60 
61 static lg::log_domain log_network("network");
62 #define DBG_NW LOG_STREAM(debug, log_network)
63 #define LOG_NW LOG_STREAM(info, log_network)
64 #define WRN_NW LOG_STREAM(warn, log_network)
65 #define ERR_NW LOG_STREAM(err, log_network)
66 // Only warnings and not errors to avoid DoS by log flooding
67 
68 namespace {
69 
70 // We store the details of a connection in a map that must be looked up by its handle.
71 // This allows a connection to be disconnected and then recovered,
72 // but the handle remains the same, so it's all seamless to the user.
73 struct connection_details {
74  connection_details(TCPsocket sock, const std::string& host, int port)
75  : sock(sock), host(host), port(port), remote_handle(0),
76  connected_at(SDL_GetTicks())
77  {}
78 
79  TCPsocket sock;
80  std::string host;
81  int port;
82 
83  // The remote handle is the handle assigned to this connection by the remote host.
84  // Is 0 before a handle has been assigned.
85  int remote_handle;
86 
87  int connected_at;
88 };
89 
90 typedef std::map<network::connection,connection_details> connection_map;
91 connection_map connections;
92 
93 network::connection connection_id = 1;
94 
95 /** Stores the time of the last server ping we received. */
96 time_t last_ping, last_ping_check = 0;
97 
98 } // end anon namespace
99 
100 static int create_connection(TCPsocket sock, const std::string& host, int port)
101 {
102  connections.insert(std::pair<network::connection,connection_details>(connection_id,connection_details(sock,host,port)));
103  return connection_id++;
104 }
105 
107 {
108  const connection_map::iterator i = connections.find(handle);
109  if(i == connections.end()) {
110  throw network::error(_("invalid network handle"));
111  }
112 
113  return i->second;
114 }
115 
117 {
118  return get_connection_details(handle).sock;
119 }
120 
122 {
123  connections.erase(handle);
124 }
125 
127 {
128  const connection_details& details = get_connection_details(handle);
129  return details.host != "" && details.remote_handle == 0;
130 }
131 
132 static void set_remote_handle(network::connection handle, int remote_handle)
133 {
134  get_connection_details(handle).remote_handle = remote_handle;
135 }
136 
137 static void check_error()
138 {
139  const TCPsocket sock = network_worker_pool::detect_error();
140  if(sock) {
141  for(connection_map::const_iterator i = connections.begin(); i != connections.end(); ++i) {
142  if(i->second.sock == sock) {
143  throw network::error(_("Client disconnected"),i->first);
144  }
145  }
146  }
147 }
148 
149 /**
150  * Check whether too much time since the last server ping has passed and we
151  * timed out. If the last check is too long ago reset the last_ping to 'now'.
152  * This happens when we "freeze" the client one way or another or we just
153  * didn't try to receive data.
154  */
155 static void check_timeout()
156 {
157  if (network::nconnections() == 0) {
158  LOG_NW << "No network connections but last_ping is: " << last_ping;
159  last_ping = 0;
160  return;
161  }
162  const time_t& now = time(nullptr);
163  DBG_NW << "Last ping: '" << last_ping << "' Current time: '" << now
164  << "' Time since last ping: " << now - last_ping << "s\n";
165  // Reset last_ping if we didn't check for the last 10s.
166  if (last_ping_check + 10 <= now) last_ping = now;
167  // This static cast is required on some build systems.
168  // (Reported in #wesnoth-dev 2012/11/24 21:10:21, log time.)
169  if ( last_ping + static_cast<time_t>(network::ping_interval + network::ping_timeout)
170  <= now )
171  {
172  time_t timeout = now - last_ping;
173  ERR_NW << "No server ping since " << timeout
174  << " seconds. Connection timed out.\n";
175  utils::string_map symbols;
176  symbols["timeout"] = std::to_string(timeout);
177  throw network::error("No server ping since " + std::to_string(timeout) + " second. "
178  "Connection timed out.");
179  }
180  last_ping_check = now;
181 }
182 
183 
184 namespace {
185 
186 SDLNet_SocketSet socket_set = 0;
187 std::set<network::connection> waiting_sockets;
188 typedef std::vector<network::connection> sockets_list;
189 sockets_list sockets;
190 
191 
192 struct partial_buffer {
193  partial_buffer() :
194  buf(),
195  upto(0)
196  {
197  }
198 
199  std::vector<char> buf;
200  size_t upto;
201 };
202 
203 TCPsocket server_socket;
204 
205 std::deque<network::connection> disconnection_queue;
206 std::set<network::connection> bad_sockets;
207 
208 network_worker_pool::manager* worker_pool_man = nullptr;
209 
210 } // end anon namespace
211 
212 namespace network {
213 
214 /**
215  * Amount of seconds after the last server ping when we assume to have timed out.
216  * When set to '0' ping timeout isn't checked.
217  * Gets set in preferences::manager according to the preferences file.
218  */
219 unsigned int ping_timeout = 0;
220 
221 connection_stats::connection_stats(int sent, int received, int connected_at)
222  : bytes_sent(sent), bytes_received(received), time_connected(SDL_GetTicks() - connected_at)
223 {}
224 
226 {
227  connection_details& details = get_connection_details(connection_num);
228  return connection_stats(get_send_stats(connection_num).total,get_receive_stats(connection_num).total,details.connected_at);
229 }
230 
231 error::error(const std::string& msg, connection sock) : game::error(msg), socket(sock)
232 {
233  if(socket) {
234  bad_sockets.insert(socket);
235  }
236 }
237 
239 {
241 }
242 
244 {
246 }
247 
248 manager::manager(size_t min_threads, size_t max_threads) : free_(true)
249 {
250  DBG_NW << "NETWORK MANAGER CALLED!\n";
251  // If the network is already being managed
252  if(socket_set) {
253  free_ = false;
254  return;
255  }
256 
257  if(SDLNet_Init() == -1) {
258  ERR_NW << "could not initialize SDLNet; throwing error..." << std::endl;
259  throw error(SDL_GetError());
260  }
261 
262  socket_set = SDLNet_AllocSocketSet(512);
263 
264  worker_pool_man = new network_worker_pool::manager(min_threads, max_threads);
265 }
266 
268 {
269  if(free_) {
270  disconnect();
271  delete worker_pool_man;
272  worker_pool_man = nullptr;
273  SDLNet_FreeSocketSet(socket_set);
274  socket_set = 0;
275  waiting_sockets.clear();
276  SDLNet_Quit();
277  }
278 }
279 
281 {
283 }
284 
285 // --- Proxy methods
287 {
288  throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
289 }
291 {
292  throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
293 }
294 
296 {
297  throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
298 }
299 
301 {
302  throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
303 }
304 
306 {
307  throw std::runtime_error("Proxy not available while using SDL_net. Use ANA instead.");
308 }
309 // --- End Proxy methods
310 
311 server_manager::server_manager(int port, CREATE_SERVER create_server) : free_(false), connection_(0)
312 {
313  if(create_server != NO_SERVER && !server_socket) {
314  try {
315  connection_ = connect("",port);
316  server_socket = get_socket(connection_);
317  } catch(network::error&) {
318  if(create_server == MUST_CREATE_SERVER) {
319  throw;
320  } else {
321  return;
322  }
323  }
324 
325  DBG_NW << "server socket initialized: " << server_socket << "\n";
326  free_ = true;
327  }
328 }
329 
331 {
332  stop();
333 }
334 
336 {
337  if(free_) {
338  SDLNet_TCP_Close(server_socket);
340  server_socket = 0;
341  free_ = false;
342  }
343 }
344 
346 {
347  return server_socket != nullptr;
348 }
349 
350 size_t nconnections()
351 {
352  return sockets.size();
353 }
354 
355 bool is_server()
356 {
357  return server_socket != 0;
358 }
359 
360 namespace {
361 
362 class connect_operation : public threading::async_operation
363 {
364 public:
365  connect_operation(const std::string& host, int port) : host_(host), port_(port), error_(), connect_(0)
366  {}
367 
368  void check_error();
369  void run();
370 
371  network::connection result() const { return connect_; }
372 
373 private:
375  int port_;
378 };
379 
381 {
382  if(!error_.empty()) {
383  throw error(error_);
384  }
385 }
386 
387 namespace {
388  struct _TCPsocket {
389  int ready;
391  IPaddress remoteAddress;
392  IPaddress localAddress;
393  int sflag;
394  };
395 } // end namespace
396 
397 void connect_operation::run()
398 {
399  char* const hostname = host_.empty() ? nullptr : const_cast<char*>(host_.c_str());
400  IPaddress ip;
401  if(SDLNet_ResolveHost(&ip,hostname,port_) == -1) {
402  error_ = N_("Could not connect to host.");
403  return;
404  }
405 
406  TCPsocket sock = SDLNet_TCP_Open(&ip);
407  if(!sock) {
408  error_ = hostname == nullptr
409  ? "Could not bind to port"
410  : N_("Could not connect to host.");
411  return;
412  }
413  _TCPsocket* raw_sock = reinterpret_cast<_TCPsocket*>(sock);
414 #ifdef TCP_NODELAY
415  //set TCP_NODELAY to 0 because SDL_Net turns it off, causing packet
416  //flooding!
417  {
418  int no = 0;
419  setsockopt(raw_sock->channel, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&no), sizeof(no));
420  }
421 #endif
422 
423 // Use non blocking IO
424 #if defined(_WIN32) || defined(__WIN32__) || defined(WIN32)
425  {
426  unsigned long mode = 1;
427  ioctlsocket (raw_sock->channel, FIONBIO, &mode);
428  }
429 #else
430  int flags;
431  flags = fcntl(raw_sock->channel, F_GETFL, 0);
432 #if defined(O_NONBLOCK)
433  flags |= O_NONBLOCK;
434 #elif defined(O_NDELAY)
435  flags |= O_NDELAY;
436 #elif defined(FNDELAY)
437  flags |= FNDELAY;
438 #endif
439  if (fcntl(raw_sock->channel, F_SETFL, flags) == -1) {
440  error_ = "Could not make socket non-blocking: " + std::string(strerror(errno));
441  SDLNet_TCP_Close(sock);
442  return;
443  }
444 #endif
445 
446  // If this is a server socket
447  if(hostname == nullptr) {
448  const threading::lock l(get_mutex());
449  connect_ = create_connection(sock,"",port_);
450  return;
451  }
452 
453  // Send data telling the remote host that this is a new connection
454  union
455  {
456  char data[4] ALIGN_4;
457  Uint32 num;
458  } buf;
459  SDLNet_Write32(0, &buf);
460  const int nbytes = SDLNet_TCP_Send(sock,&buf,4);
461  if(nbytes != 4) {
462  SDLNet_TCP_Close(sock);
463  error_ = "Could not send initial handshake";
464  return;
465  }
466 
467  // No blocking operations from here on
468  const threading::lock l(get_mutex());
469  DBG_NW << "sent handshake...\n";
470 
471  if(is_aborted()) {
472  DBG_NW << "connect operation aborted by calling thread\n";
473  SDLNet_TCP_Close(sock);
474  return;
475  }
476 
477  // Allocate this connection a connection handle
479 
480  const int res = SDLNet_TCP_AddSocket(socket_set,sock);
481  if(res == -1) {
482  SDLNet_TCP_Close(sock);
483  error_ = "Could not add socket to socket set";
484  return;
485  }
486 
487  waiting_sockets.insert(connect_);
488 
489  sockets.push_back(connect_);
490 
491  while(!notify_finished()) {};
492 }
493 
494 
495 } // end namespace
496 
497 
498 connection connect(const std::string& host, int port)
499 {
500  connect_operation op(host,port);
501  op.run();
502  op.check_error();
503  return op.result();
504 }
505 
506 connection connect(const std::string& host, int port, threading::waiter& waiter)
507 {
508  const threading::async_operation_ptr op(new connect_operation(host,port));
509  const connect_operation::RESULT res = op->execute(op, waiter);
510  if(res == connect_operation::ABORTED) {
511  return 0;
512  }
513 
514  static_cast<connect_operation*>(op.get())->check_error();
515  return static_cast<connect_operation*>(op.get())->result();
516 }
517 
518 namespace {
519 
520 connection accept_connection_pending(std::vector<TCPsocket>& pending_sockets,
521  SDLNet_SocketSet& pending_socket_set)
522 {
523  DBG_NW << "pending socket activity...\n";
524 
525  std::vector<TCPsocket>::iterator i = pending_sockets.begin();
526  while (i != pending_sockets.end() && !SDLNet_SocketReady(*i)) ++i;
527 
528  if (i == pending_sockets.end()) return 0;
529 
530  // Receive the 4 bytes telling us if they're a new connection
531  // or trying to recover a connection
532  union
533  {
534  char data[4] ALIGN_4;
535  Uint32 num;
536  } buf;
537 
538  const TCPsocket psock = *i;
539  SDLNet_TCP_DelSocket(pending_socket_set,psock);
540  pending_sockets.erase(i);
541 
542  DBG_NW << "receiving data from pending socket...\n";
543 
544  const int len = SDLNet_TCP_Recv(psock,&buf,4);
545  if(len != 4) {
546  WRN_NW << "pending socket disconnected" << std::endl;
547  SDLNet_TCP_Close(psock);
548  return 0;
549  }
550 
551  const int handle = SDLNet_Read32(&buf);
552 
553  DBG_NW << "received handshake from client: '" << handle << "'\n";
554 
555  const int res = SDLNet_TCP_AddSocket(socket_set,psock);
556  if(res == -1) {
557  ERR_NW << "SDLNet_GetError(): " << SDLNet_GetError() << std::endl;
558  SDLNet_TCP_Close(psock);
559 
560  throw network::error(_("Could not add socket to socket set"));
561  }
562 
563  const connection connect = create_connection(psock,"",0);
564 
565  // Send back their connection number
566  SDLNet_Write32(connect, &buf);
567  const int nbytes = SDLNet_TCP_Send(psock,&buf,4);
568  if(nbytes != 4) {
569  SDLNet_TCP_DelSocket(socket_set,psock);
570  SDLNet_TCP_Close(psock);
571  remove_connection(connect);
572  throw network::error(_("Could not send initial handshake"));
573  }
574 
575  waiting_sockets.insert(connect);
576  sockets.push_back(connect);
577  return connect;
578 }
579 
580 } //anon namespace
581 
583 {
584  if(!server_socket) {
585  return 0;
586  }
587 
588  // A connection isn't considered 'accepted' until it has sent its initial handshake.
589  // The initial handshake is a 4 byte value, which is 0 for a new connection,
590  // or the handle of the connection if it's trying to recover a lost connection.
591 
592  /**
593  * A list of all the sockets which have connected,
594  * but haven't had their initial handshake received.
595  */
596  static std::vector<TCPsocket> pending_sockets;
597  static SDLNet_SocketSet pending_socket_set = 0;
598 
599  const TCPsocket sock = SDLNet_TCP_Accept(server_socket);
600  if(sock) {
601 #if !defined(_WIN32) && !defined(__WIN32__) && !defined (WIN32)
602  _TCPsocket* raw_sock = reinterpret_cast<_TCPsocket*>(sock);
603  int fd_flags = fcntl(raw_sock->channel, F_GETFD, 0);
604  fd_flags |= FD_CLOEXEC;
605  if (fcntl(raw_sock->channel, F_SETFD, fd_flags) == -1) {
606  WRN_NW << "could not make socket " << sock << " close-on-exec: " << strerror(errno);
607  } else {
608  DBG_NW << "made socket " << sock << " close-on-exec\n";
609  }
610 #endif
611 
612  DBG_NW << "received connection. Pending handshake...\n";
613 
614  if(pending_socket_set == 0) {
615  pending_socket_set = SDLNet_AllocSocketSet(32);
616  }
617 
618  if(pending_socket_set != 0) {
619  int res = SDLNet_TCP_AddSocket(pending_socket_set,sock);
620 
621  if (res != -1) {
622  pending_sockets.push_back(sock);
623  } else {
624  ERR_NW << "Pending socket set is full! Disconnecting " << sock << " connection" << std::endl;
625  ERR_NW << "SDLNet_GetError(): " << SDLNet_GetError() << std::endl;
626 
627  SDLNet_TCP_Close(sock);
628  }
629  } else {
630  ERR_NW << "Error in SDLNet_AllocSocketSet" << std::endl;
631  }
632  }
633 
634  if(pending_socket_set == 0) {
635  return 0;
636  }
637 
638  const int set_res = SDLNet_CheckSockets(pending_socket_set,0);
639  if(set_res <= 0) {
640  return 0;
641  }
642 
643  return accept_connection_pending(pending_sockets, pending_socket_set);
644 }
645 
647 {
648  if(s == 0) {
649  while(sockets.empty() == false) {
650  assert(sockets.back() != 0);
651  while(disconnect(sockets.back()) == false) {
652  SDL_Delay(1);
653  }
654  }
655  return true;
656  }
657  if (!is_server()) last_ping = 0;
658 
659  const connection_map::iterator info = connections.find(s);
660  if(info != connections.end()) {
661  if (info->second.sock == server_socket)
662  {
663  return true;
664  }
665  if (!network_worker_pool::close_socket(info->second.sock)) {
666  return false;
667  }
668  }
669 
670  bad_sockets.erase(s);
671 
672  std::deque<network::connection>::iterator dqi = std::find(disconnection_queue.begin(),disconnection_queue.end(),s);
673  if(dqi != disconnection_queue.end()) {
674  disconnection_queue.erase(dqi);
675  }
676 
677  const sockets_list::iterator i = std::find(sockets.begin(),sockets.end(),s);
678  if(i != sockets.end()) {
679  sockets.erase(i);
680 
681  const TCPsocket sock = get_socket(s);
682 
683  waiting_sockets.erase(s);
684  SDLNet_TCP_DelSocket(socket_set,sock);
685  SDLNet_TCP_Close(sock);
686 
688  } else {
689  if(sockets.size() == 1) {
690  DBG_NW << "valid socket: " << static_cast<int>(*sockets.begin()) << "\n";
691  }
692  }
693 
694  return true;
695 }
696 
698 {
699  disconnection_queue.push_back(sock);
700 }
701 
703 {
704  unsigned int start_ticks = SDL_GetTicks();
705  while(true) {
706  const connection res = receive_data(
707  cfg,connection_num, bandwidth_in);
708  if(res != 0) {
709  return res;
710  }
711 
712  if(timeout > SDL_GetTicks() - start_ticks) {
713  SDL_Delay(1);
714  }
715  else
716  {
717  break;
718  }
719 
720  }
721 
722  return 0;
723 }
724 
726 {
727  if(!socket_set) {
728  return 0;
729  }
730 
731  check_error();
732 
733  if(disconnection_queue.empty() == false) {
734  const network::connection sock = disconnection_queue.front();
735  disconnection_queue.pop_front();
736  throw error("",sock);
737  }
738 
739  if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
740  return 0;
741  }
742 
743  if(sockets.empty()) {
744  return 0;
745  }
746 
747  const int res = SDLNet_CheckSockets(socket_set,0);
748 
749  for(std::set<network::connection>::iterator i = waiting_sockets.begin(); res != 0 && i != waiting_sockets.end(); ) {
750  connection_details& details = get_connection_details(*i);
751  const TCPsocket sock = details.sock;
752  if(SDLNet_SocketReady(sock)) {
753 
754  // See if this socket is still waiting for it to be assigned its remote handle.
755  // If it is, then the first 4 bytes must be the remote handle.
756  if(is_pending_remote_handle(*i)) {
757  union {
758  char data[4] ALIGN_4;
759  } buf;
760  int len = SDLNet_TCP_Recv(sock,&buf,4);
761  if(len != 4) {
762  throw error("Remote host disconnected",*i);
763  }
764 
765  const int remote_handle = SDLNet_Read32(&buf);
766  set_remote_handle(*i,remote_handle);
767 
768  continue;
769  }
770 
771  waiting_sockets.erase(i++);
772  SDLNet_TCP_DelSocket(socket_set,sock);
774  } else {
775  ++i;
776  }
777  }
778 
779 
780  TCPsocket sock = connection_num == 0 ? 0 : get_socket(connection_num);
781  TCPsocket s = sock;
782  bandwidth_in_ptr temp;
783  if (!bandwidth_in)
784  {
785  bandwidth_in = &temp;
786  }
787  try {
788  sock = network_worker_pool::get_received_data(sock,cfg, *bandwidth_in);
789  } catch(const config::error& e) {
790  TCPsocket const * err_sock = boost::get_error_info<tcpsocket_info>(e);
791  if(err_sock == nullptr)
792  throw;
793  connection err_connection = 0;
794  for(connection_map::const_iterator i = connections.begin(); i != connections.end(); ++i) {
795  if(i->second.sock == *err_sock) {
796  err_connection = i->first;
797  }
798  }
799  if(err_connection) {
800  throw e << connection_info(err_connection);
801  }
802  throw;
803  }
804  if (sock == nullptr) {
805  if (!is_server() && last_ping != 0 && ping_timeout != 0)
806  {
807  if (connection_num == 0)
808  {
809  s = get_socket(sockets.back());
810  }
812  {
813  check_timeout();
814  }
815  }
816  return 0;
817  }
818 
819  int set_res = SDLNet_TCP_AddSocket(socket_set,sock);
820  if (set_res == -1)
821  {
822  ERR_NW << "Socket set is full! Disconnecting " << sock << " connection" << std::endl;
823  SDLNet_TCP_Close(sock);
824  return 0;
825  }
826 
827  connection result = 0;
828  for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
829  if(j->second.sock == sock) {
830  result = j->first;
831  break;
832  }
833  }
834  if(!cfg.empty()) {
835  DBG_NW << "RECEIVED from: " << result << ": " << cfg;
836  }
837 
838  assert(result != 0);
839  waiting_sockets.insert(result);
840  if(!is_server()) {
841  const time_t& now = time(nullptr);
842  if (cfg.has_attribute("ping")) {
843  LOG_NW << "Lag: " << (now - lexical_cast<time_t>(cfg["ping"])) << "\n";
844  last_ping = now;
845  } else if (last_ping != 0) {
846  last_ping = now;
847  }
848  }
849  return result;
850 }
851 
853 {
854  if(!socket_set) {
855  return 0;
856  }
857 
858  check_error();
859 
860  if(disconnection_queue.empty() == false) {
861  const network::connection sock = disconnection_queue.front();
862  disconnection_queue.pop_front();
863  throw error("",sock);
864  }
865 
866  if(bad_sockets.count(0)) {
867  return 0;
868  }
869 
870  if(sockets.empty()) {
871  return 0;
872  }
873 
874  const int res = SDLNet_CheckSockets(socket_set,0);
875 
876  for(std::set<network::connection>::iterator i = waiting_sockets.begin(); res != 0 && i != waiting_sockets.end(); ) {
877  connection_details& details = get_connection_details(*i);
878  const TCPsocket sock = details.sock;
879  if(SDLNet_SocketReady(sock)) {
880 
881  // See if this socket is still waiting for it to be assigned its remote handle.
882  // If it is, then the first 4 bytes must be the remote handle.
883  if(is_pending_remote_handle(*i)) {
884  union {
885  char data[4] ALIGN_4;
886  } buf;
887  int len = SDLNet_TCP_Recv(sock,&buf,4);
888  if(len != 4) {
889  throw error("Remote host disconnected",*i);
890  }
891 
892  const int remote_handle = SDLNet_Read32(&buf);
893  set_remote_handle(*i,remote_handle);
894 
895  continue;
896  }
897 
898  waiting_sockets.erase(i++);
899  SDLNet_TCP_DelSocket(socket_set,sock);
901  } else {
902  ++i;
903  }
904  }
905 
906 
907  TCPsocket sock = network_worker_pool::get_received_data(buf);
908  if (sock == nullptr) {
909  return 0;
910  }
911 
912  {
913  bandwidth_in_ptr temp;
914  if (!bandwidth_in)
915  {
916  bandwidth_in = &temp;
917  }
918  const int headers = 4;
919  bandwidth_in->reset(new network::bandwidth_in(buf.size() + headers));
920  }
921 
922  int set_res = SDLNet_TCP_AddSocket(socket_set,sock);
923 
924  if (set_res == -1)
925  {
926  ERR_NW << "Socket set is full! Disconnecting " << sock << " connection" << std::endl;
927  SDLNet_TCP_Close(sock);
928  return 0;
929  }
930  connection result = 0;
931  for(connection_map::const_iterator j = connections.begin(); j != connections.end(); ++j) {
932  if(j->second.sock == sock) {
933  result = j->first;
934  break;
935  }
936  }
937 
938  assert(result != 0);
939  waiting_sockets.insert(result);
940  return result;
941 }
946  int in_bytes;
947  int day;
948  const static size_t type_width = 16;
949  const static size_t packet_width = 7;
950  const static size_t bytes_width = 10;
952  {
953  out_packets += a.out_packets;
954  out_bytes += a.out_bytes;
955  in_packets += a.in_packets;
956  in_bytes += a.in_bytes;
957 
958  return *this;
959  }
960 };
961 typedef std::map<const std::string, bandwidth_stats> bandwidth_map;
962 typedef std::vector<bandwidth_map> hour_stats_vector;
963 
964 static hour_stats_vector hour_stats(24);
965 
966 
968 {
969  time_t now = time(0);
970  struct tm * timeinfo = localtime(&now);
971  int hour = timeinfo->tm_hour;
972  int day = timeinfo->tm_mday;
973  assert(hour < 24 && hour >= 0);
974  std::pair<bandwidth_map::iterator,bool> insertion = hour_stats[hour].insert(std::make_pair(packet_type, bandwidth_stats()));
975  bandwidth_map::iterator inserted = insertion.first;
976  if (!insertion.second && day != inserted->second.day)
977  {
978  // clear previuos day stats
979  hour_stats[hour].clear();
980  //insert again to cleared map
981  insertion = hour_stats[hour].insert(std::make_pair(packet_type, bandwidth_stats()));
982  inserted = insertion.first;
983  }
984 
985  inserted->second.day = day;
986  return inserted;
987 }
988 
990 
991 
993  bandwidth_stats_output(std::stringstream& ss) : ss_(ss), totals_(new bandwidth_stats())
994  {}
995  void operator()(const bandwidth_map::value_type& stats)
996  {
997  // name
998  ss_ << " " << std::setw(bandwidth_stats::type_width) << stats.first << "| "
999  << std::setw(bandwidth_stats::packet_width)<< stats.second.out_packets << "| "
1000  << std::setw(bandwidth_stats::bytes_width) << stats.second.out_bytes/1024 << "| "
1001  << std::setw(bandwidth_stats::packet_width)<< stats.second.in_packets << "| "
1002  << std::setw(bandwidth_stats::bytes_width) << stats.second.in_bytes/1024 << "\n";
1003  *totals_ += stats.second;
1004  }
1006  {
1007  (*this)(std::make_pair(std::string("total"), *totals_));
1008  }
1009  private:
1010  std::stringstream& ss_;
1011  bandwidth_stats_ptr totals_;
1012 };
1013 
1015 {
1017  for (int hour = 0; hour < 24; ++hour)
1018  {
1019  result += get_bandwidth_stats(hour);
1020  }
1021  return result;
1022 }
1023 
1025 {
1026  time_t now = time(0);
1027  struct tm * timeinfo = localtime(&now);
1028  int hour = timeinfo->tm_hour - 1;
1029  if (hour < 0)
1030  hour = 23;
1031  return get_bandwidth_stats(hour);
1032 }
1033 
1035 {
1036  assert(hour < 24 && hour >= 0);
1037  std::stringstream ss;
1038 
1039  ss << "Hour stat starting from " << hour << "\n " << std::left << std::setw(bandwidth_stats::type_width) << "Type of packet" << "| "
1040  << std::setw(bandwidth_stats::packet_width)<< "out #" << "| "
1041  << std::setw(bandwidth_stats::bytes_width) << "out KiB" << "| "
1042  << std::setw(bandwidth_stats::packet_width)<< "in #" << "| "
1043  << std::setw(bandwidth_stats::bytes_width) << "in KiB" << "\n";
1044 
1045  bandwidth_stats_output outputer(ss);
1046  std::for_each(hour_stats[hour].begin(), hour_stats[hour].end(), outputer);
1047 
1048  outputer.output_totals();
1049  return ss.str();
1050 }
1051 
1052 void add_bandwidth_out(const std::string& packet_type, size_t len)
1053 {
1055  itor->second.out_bytes += len;
1056  ++(itor->second.out_packets);
1057 }
1058 
1059 void add_bandwidth_in(const std::string& packet_type, size_t len)
1060 {
1062  itor->second.in_bytes += len;
1063  ++(itor->second.in_packets);
1064 }
1065 
1067  {
1069  }
1070 
1071 void send_file(const std::string& filename, connection connection_num, const std::string& packet_type)
1072 {
1073  assert(connection_num > 0);
1074  if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
1075  return;
1076  }
1077 
1078  const connection_map::iterator info = connections.find(connection_num);
1079  if (info == connections.end()) {
1080  ERR_NW << "Error: socket: " << connection_num
1081  << "\tnot found in connection_map. Not sending...\n";
1082  return;
1083  }
1084 
1085  const int size = filesystem::file_size(filename);
1086 
1087  if(size < 0) {
1088  ERR_NW << "Could not determine size of file " << filename << ", not sending." << std::endl;
1089  return;
1090  }
1091 
1092  const int packet_headers = 4;
1093  add_bandwidth_out(packet_type, size + packet_headers);
1094  network_worker_pool::queue_file(info->second.sock, filename);
1095 
1096 }
1097 
1098 size_t send_data(const config& cfg, connection connection_num, const std::string& packet_type)
1099 {
1100  DBG_NW << "in send_data()...\n";
1101 
1102  if(cfg.empty()) {
1103  return 0;
1104  }
1105 
1106  if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
1107  return 0;
1108  }
1109 
1110 // log_scope2(network, "sending data");
1111  if(!connection_num) {
1112  DBG_NW << "sockets: " << sockets.size() << "\n";
1113  size_t size = 0;
1114  for(sockets_list::const_iterator i = sockets.begin();
1115  i != sockets.end(); ++i) {
1116  DBG_NW << "server socket: " << server_socket << "\ncurrent socket: " << *i << "\n";
1117  size = send_data(cfg,*i, packet_type);
1118  }
1119  return size;
1120  }
1121 
1122  const connection_map::iterator info = connections.find(connection_num);
1123  if (info == connections.end()) {
1124  ERR_NW << "Error: socket: " << connection_num
1125  << "\tnot found in connection_map. Not sending...\n";
1126  return 0;
1127  }
1128 
1129  LOG_NW << "SENDING to: " << connection_num << ": " << cfg;
1130  return network_worker_pool::queue_data(info->second.sock, cfg, packet_type);
1131 }
1132 
1133 void send_raw_data(const char* buf, int len, connection connection_num, const std::string& packet_type)
1134 {
1135  if(len == 0) {
1136  return;
1137  }
1138 
1139  if(bad_sockets.count(connection_num) || bad_sockets.count(0)) {
1140  return;
1141  }
1142 
1143  if(!connection_num) {
1144  for(sockets_list::const_iterator i = sockets.begin();
1145  i != sockets.end(); ++i) {
1146  send_raw_data(buf, len, *i, packet_type);
1147  }
1148  return;
1149  }
1150 
1151  const connection_map::iterator info = connections.find(connection_num);
1152  if (info == connections.end()) {
1153  ERR_NW << "Error: socket: " << connection_num
1154  << "\tnot found in connection_map. Not sending...\n";
1155  return;
1156  }
1157  const int packet_headers = 4;
1158  add_bandwidth_out(packet_type, len + packet_headers);
1159 
1160  network_worker_pool::queue_raw_data(info->second.sock, buf, len);
1161 }
1162 
1164 {
1165  check_error();
1166 }
1167 
1168 void send_data_all_except(const config& cfg, connection connection_num, const std::string& packet_type)
1169 {
1170  for(sockets_list::const_iterator i = sockets.begin(); i != sockets.end(); ++i) {
1171  if(*i == connection_num) {
1172  continue;
1173  }
1174 
1175  send_data(cfg,*i, packet_type);
1176  }
1177 }
1178 
1180 {
1181  std::stringstream str;
1182  const IPaddress* const ip = SDLNet_TCP_GetPeerAddress(get_socket(connection_num));
1183  if(ip != nullptr) {
1184  const unsigned char* buf = reinterpret_cast<const unsigned char*>(&ip->host);
1185  for(int i = 0; i != sizeof(ip->host); ++i) {
1186  str << int(buf[i]);
1187  if(i+1 != sizeof(ip->host)) {
1188  str << '.';
1189  }
1190  }
1191 
1192  }
1193 
1194  return str.str();
1195 }
1196 
1198 {
1199  return network_worker_pool::get_current_transfer_stats(handle == 0 ? get_socket(sockets.back()) : get_socket(handle)).first;
1200 }
1202 {
1203  const statistics result = network_worker_pool::get_current_transfer_stats(handle == 0 ? get_socket(sockets.back()) : get_socket(handle)).second;
1204 
1205  return result;
1206 }
1207 
1208 } // end namespace network
void set_raw_data_only()
Definition: network.cpp:280
High level network layer for config object transport.
Definition: network.cpp:212
#define DBG_NW
Definition: network.cpp:62
SOCKET channel
Definition: network.cpp:390
static void set_remote_handle(network::connection handle, int remote_handle)
Definition: network.cpp:132
connection connection_
Definition: network.hpp:97
void operator()(const bandwidth_map::value_type &stats)
Definition: network.cpp:995
#define WRN_NW
Definition: network.cpp:64
#define ERR_NW
Definition: network.cpp:65
void receive_data(TCPsocket sock)
Function to asynchronously received data to the given socket.
static hour_stats_vector hour_stats(24)
std::stringstream & ss_
Definition: network.cpp:1010
const int ping_interval
Minimum interval between pings.
Definition: network.hpp:306
static lg::log_domain log_network("network")
IPaddress remoteAddress
Definition: network.cpp:391
void send_data_all_except(const config &cfg, connection connection_num, const std::string &packet_type)
Function to send data to all peers except 'connection_num'.
Definition: network.cpp:1168
void add_bandwidth_in(const std::string &packet_type, size_t len)
Definition: network.cpp:1059
void queue_disconnect(network::connection sock)
Function to queue a disconnection.
Definition: network.cpp:697
void set_proxy_user(const std::string &)
Set the user to authenticate with the proxy.
Definition: network.cpp:300
static l_noret error(LoadState *S, const char *why)
Definition: lundump.cpp:29
logger & info()
Definition: log.cpp:91
static TCPsocket get_socket(network::connection handle)
Definition: network.cpp:116
void queue_raw_data(TCPsocket sock, const char *buf, int len)
static void check_timeout()
Check whether too much time since the last server ping has passed and we timed out.
Definition: network.cpp:155
static bandwidth_map::iterator add_bandwidth_entry(const std::string &packet_type)
Definition: network.cpp:967
std::string type_
Definition: network.hpp:203
connection receive_data(config &cfg, connection connection_num, unsigned int timeout, bandwidth_in_ptr *bandwidth_in)
Definition: network.cpp:702
void process_send_queue(connection, size_t)
Function to send any data that is in a connection's send_queue, up to a maximum of 'max_size' bytes �...
Definition: network.cpp:1163
connection socket
Definition: network.hpp:265
#define LOG_NW
Definition: network.cpp:63
boost::shared_ptr< bandwidth_stats > bandwidth_stats_ptr
Definition: network.cpp:989
GLint GLenum GLsizei GLint GLsizei const GLvoid * data
Definition: glew.h:1347
bool empty() const
Definition: config.cpp:1105
To lexical_cast(From value)
Lexical cast converts one type to another.
GLenum mode
Definition: glew.h:2390
Definitions for the interface to Wesnoth Markup Language (WML).
void enable_connection_through_proxy()
Attempt to connect through a proxy (as opposed to directly.)
Definition: network.cpp:286
static connection_details & get_connection_details(network::connection handle)
Definition: network.cpp:106
bool is_running() const
Definition: network.cpp:345
void set_proxy_address(const std::string &)
Set the address of the proxy.
Definition: network.cpp:290
GLdouble l
Definition: glew.h:6966
int ready
Definition: network.cpp:389
size_t queue_data(TCPsocket sock, const config &buf, const std::string &packet_type)
bool is_locked(const TCPsocket sock)
TCPsocket get_received_data(TCPsocket sock, config &cfg, network::bandwidth_in_ptr &bandwidth_in)
void send_file(const std::string &filename, connection connection_num, const std::string &packet_type)
Definition: network.cpp:1071
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:82
std::vector< bandwidth_map > hour_stats_vector
Definition: network.cpp:962
#define ALIGN_4
Aligns a variable on a 4 byte boundary.
GLuint GLuint end
Definition: glew.h:1221
GLuint64EXT * result
Definition: glew.h:10727
bandwidth_stats_output(std::stringstream &ss)
Definition: network.cpp:993
std::map< std::string, t_string > string_map
static int create_connection(TCPsocket sock, const std::string &host, int port)
Definition: network.cpp:100
void set_proxy_port(const std::string &)
Set the port of the proxy.
Definition: network.cpp:295
network::connection connect_
Definition: network.cpp:377
manager(size_t min_threads=1, size_t max_threads=0)
Definition: network.cpp:248
pending_statistics get_pending_stats()
Definition: network.cpp:243
void queue_file(TCPsocket sock, const std::string &filename)
GLenum GLsizei len
Definition: glew.h:5662
GLboolean GLboolean GLboolean GLboolean a
Definition: glew.h:7319
GLuint num
Definition: glew.h:2552
int port_
Definition: network.cpp:375
GLenum GLuint GLsizei const char * buf
Definition: glew.h:2498
unsigned int ping_timeout
Amount of seconds after the last server ping when we assume to have timed out.
Definition: network.cpp:219
void set_proxy_password(const std::string &)
Set the password to authenticate with the proxy.
Definition: network.cpp:305
typedef int(WINAPI *PFNWGLRELEASEPBUFFERDCARBPROC)(HPBUFFERARB hPbuffer
Templates and utility-routines for strings and numbers.
cl_event GLbitfield flags
Definition: glew.h:3070
bandwidth_stats_ptr totals_
Definition: network.cpp:1011
connection_stats(int sent, int received, int connected_at)
Definition: network.cpp:221
void send_raw_data(const char *buf, int len, connection connection_num, const std::string &packet_type)
Definition: network.cpp:1133
network::pending_statistics get_pending_stats()
CREATE_SERVER
Parameter to pass to the constructor.
Definition: network.hpp:84
statistics get_send_stats(connection handle)
Function to see the number of bytes being processed on the current socket.
Definition: network.cpp:1197
size_t send_data(const config &cfg, connection connection_num, const std::string &packet_type)
Function to send data down a given connection, or broadcast to all peers if connection_num is 0...
Definition: network.cpp:1098
GLuint res
Definition: glew.h:9258
bandwidth_stats & operator+=(const bandwidth_stats &a)
Definition: network.cpp:951
std::map< std::string, tfilter >::iterator itor
Definition: filter.cpp:199
GLint left
Definition: glew.h:5907
GLbitfield GLuint64 timeout
Definition: glew.h:4717
Will throw exception on failure.
Definition: network.hpp:84
static void remove_connection(network::connection handle)
Definition: network.cpp:121
std::map< const std::string, bandwidth_stats > bandwidth_map
Definition: network.cpp:961
server_manager(int port, CREATE_SERVER create_server=MUST_CREATE_SERVER)
Definition: network.cpp:311
size_t i
Definition: function.cpp:1057
connection accept_connection()
Function to accept a connection from a remote host.
Definition: network.cpp:582
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:112
static const size_t type_width
Definition: network.cpp:948
std::string host_
Definition: network.cpp:374
Declarations for File-IO.
static const size_t packet_width
Definition: network.cpp:949
#define N_(String)
Definition: gettext.hpp:90
static void check_error()
Definition: network.cpp:137
bool disconnect(connection s)
Function to disconnect from a certain host, or close all connections if connection_num is 0...
Definition: network.cpp:646
int file_size(const std::string &fname)
Returns the size of a file, or -1 if the file doesn't exist.
std::string error_
Definition: network.cpp:376
#define SOCKET
Definition: network.cpp:56
GLsizeiptr size
Definition: glew.h:1649
size_t nconnections()
The number of peers we are connected to.
Definition: network.cpp:350
boost::error_info< struct tag_connum, connection > connection_info
Definition: network.hpp:271
IPaddress localAddress
Definition: network.cpp:392
connection connect(const std::string &host, int port)
Function to attempt to connect to a remote host.
Definition: network.cpp:498
bool has_attribute(const std::string &key) const
Definition: config.cpp:514
std::string ip_address(connection connection_num)
Function to get the remote ip address of a socket.
Definition: network.cpp:1179
static const size_t bytes_width
Definition: network.cpp:950
bool find(E event, F functor)
Tests whether an event handler is available.
Standard logging facilities (interface).
GLint * first
Definition: glew.h:1496
int connection
Definition: network.hpp:74
std::string get_bandwidth_stats_all()
Definition: network.cpp:1014
#define e
bool is_server()
If we are currently accepting connections.
Definition: network.cpp:355
std::string::const_iterator iterator
Definition: tokenizer.hpp:21
std::string get_bandwidth_stats()
Definition: network.cpp:1024
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:83
statistics get_receive_stats(connection handle)
Definition: network.cpp:1201
GLdouble s
Definition: glew.h:1358
int sflag
Definition: network.cpp:393
bool close_socket(TCPsocket sock)
GLsizei const GLcharARB ** string
Definition: glew.h:4503
void disconnect()
Definition: network.cpp:238
static bool is_pending_remote_handle(network::connection handle)
Definition: network.cpp:126
connection_stats get_connection_stats(connection connection_num)
Definition: network.cpp:225
boost::shared_ptr< halo_record > handle
Definition: halo.hpp:34
std::pair< network::statistics, network::statistics > get_current_transfer_stats(TCPsocket sock)
void add_bandwidth_out(const std::string &packet_type, size_t len)
Definition: network.cpp:1052