1 /******************************************************************************
2  *  Warmux is a convivial mass murder game.
3  *  Copyright (C) 2001-2011 Warmux Team.
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  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
18  ******************************************************************************
19  * Socket abstraction
20  *****************************************************************************/
21 
22 #include <WARMUX_error.h>
23 #include <SDL_thread.h>
24 #include <SDL_timer.h>
25 #include <WARMUX_socket.h>
26 
27 #include <iostream>
28 #include <sys/types.h>
29 #include <errno.h>
30 #include "extSDL_net.h"
31 //-----------------------------------------------------------------------------
32 
33 // bigger packets are sent by a fake client
34 static const int MAX_VALID_PACKET_SIZE = 250*1024;
35 
print_net_error(const std::string & text)36 static void print_net_error(const std::string& text)
37 {
38   int err = errno;
39   fprintf(stderr, "Error in %s: SYS: %s - SDL: %s\n", text.c_str(), strerror(err), SDLNet_GetError());
40 }
41 
42 //-----------------------------------------------------------------------------
43 // static method
GetSocketSet(uint maxsockets)44 WSocketSet* WSocketSet::GetSocketSet(uint maxsockets)
45 {
46   SDLNet_SocketSet sdl_socket_set = SDLNet_AllocSocketSet(maxsockets);
47   if (!sdl_socket_set) {
48     print_net_error("SDLNet_AllocSocketSet");
49     return NULL;
50   }
51   return new WSocketSet(maxsockets, sdl_socket_set);
52 }
53 
54 
WSocketSet(uint maxsockets,SDLNet_SocketSet sdl_socket_set)55 WSocketSet::WSocketSet(uint maxsockets, SDLNet_SocketSet sdl_socket_set) :
56   max_nb_sockets(maxsockets),
57   socket_set(sdl_socket_set),
58   lock(SDL_CreateMutex())
59 {
60 }
61 
~WSocketSet()62 WSocketSet::~WSocketSet()
63 {
64   ASSERT(sockets.empty());
65 
66   SDLNet_FreeSocketSet(socket_set);
67   SDL_DestroyMutex(lock);
68 }
69 
Lock()70 void WSocketSet::Lock()
71 {
72   SDL_LockMutex(lock);
73 }
74 
UnLock()75 void WSocketSet::UnLock()
76 {
77   SDL_UnlockMutex(lock);
78 }
79 
AddSocket(WSocket * socket)80 bool WSocketSet::AddSocket(WSocket* socket)
81 {
82   Lock();
83 
84   ASSERT(socket_set != NULL);
85 
86   if (!socket->AddToSocketSet(this))
87     return false;
88 
89   sockets.push_back(socket);
90 
91   UnLock();
92 
93   return true;
94 }
95 
RemoveSocket(WSocket * socket)96 void WSocketSet::RemoveSocket(WSocket* socket)
97 {
98   Lock();
99 
100   ASSERT(socket_set != NULL);
101   sockets.remove(socket);
102 
103   socket->RemoveFromSocketSet();
104 
105   UnLock();
106 }
107 
GetSockets()108 std::list<WSocket*>& WSocketSet::GetSockets()
109 {
110   return sockets;
111 }
112 
CheckActivity(int timeout)113 int WSocketSet::CheckActivity(int timeout)
114 {
115   if (sockets.empty()) {
116     SDL_Delay(timeout);
117     return 0;
118   }
119   return SDLNet_CheckSockets(socket_set, timeout);
120 }
121 
MaxNbSockets() const122 uint WSocketSet::MaxNbSockets() const
123 {
124   return max_nb_sockets;
125 }
126 
NbSockets() const127 uint WSocketSet::NbSockets() const
128 {
129   return sockets.size();
130 }
131 
132 //-----------------------------------------------------------------------------
133 //-----------------------------------------------------------------------------
134 
WSocket(TCPsocket _socket,WSocketSet * _socket_set)135 WSocket::WSocket(TCPsocket _socket, WSocketSet* _socket_set) :
136   socket(_socket),
137   socket_set(_socket_set),
138   lock(SDL_CreateMutex()),
139   using_tmp_socket_set(false),
140   m_packet(NULL),
141   m_packet_size(0),
142   m_received(0),
143   address_field_valid(false)
144 {
145   int r;
146   r = socket_set->AddSocket(this);
147   if (r == -1) {
148     print_net_error("SDLNet_TCP_AddSocket");
149     ASSERT(false);
150   }
151 }
152 
WSocket(TCPsocket _socket)153 WSocket::WSocket(TCPsocket _socket):
154   socket(_socket),
155   socket_set(NULL),
156   lock(SDL_CreateMutex()),
157   using_tmp_socket_set(false),
158   m_packet(NULL),
159   m_packet_size(0),
160   m_received(0),
161   address_field_valid(false)
162 {
163 }
164 
WSocket()165 WSocket::WSocket():
166   socket(NULL),
167   socket_set(NULL),
168   lock(SDL_CreateMutex()),
169   using_tmp_socket_set(false),
170   m_packet(NULL),
171   m_packet_size(0),
172   m_received(0),
173   address_field_valid(false)
174 {
175 }
176 
~WSocket()177 WSocket::~WSocket()
178 {
179   Disconnect();
180 
181   SDL_DestroyMutex(lock);
182 }
183 
ConnectTo(const std::string & host,const int & port)184 connection_state_t WSocket::ConnectTo(const std::string &host, const int &port)
185 {
186   connection_state_t r;
187   WNet::Init();
188 
189 #ifndef __SYMBIAN32__
190   r = WNet::CheckHost(host, port);
191   if (r != CONNECTED)
192     return r;
193 #endif
194 
195   Lock();
196   ASSERT(socket == NULL);
197 
198   IPaddress ip;
199   TCPsocket tcp_socket;
200 
201   if (SDLNet_ResolveHost(&ip, host.c_str(), (Uint16)port) != 0) {
202     print_net_error("SDLNet_ResolveHost");
203     fprintf(stderr, "SDLNet_ResolveHost: %s to %s:%i\n", SDLNet_GetError(), host.c_str(), port);
204     r = CONN_BAD_HOST;
205     goto error;
206   }
207 
208   // CheckHost opens and closes a connection to the server, so before reconnecting
209   // wait a bit, so the connection really gets closed ..
210   SDL_Delay(500);
211 
212   tcp_socket = SDLNet_TCP_Open(&ip);
213 
214   if (!tcp_socket) {
215     print_net_error("SDLNet_TCP_Open");
216     fprintf(stderr, "SDLNet_TCP_Open: %s to%s:%i\n", SDLNet_GetError(), host.c_str(), port);
217     r = CONN_REJECTED;
218     goto error;
219   }
220 
221   socket = tcp_socket;
222   r = CONNECTED;
223 
224  error:
225   UnLock();
226   return r;
227 }
228 
AcceptIncoming(const int & port)229 bool WSocket::AcceptIncoming(const int& port)
230 {
231   bool r = false;
232   WNet::Init();
233 
234   Lock();
235   ASSERT(socket == NULL);
236 
237   IPaddress ip;
238   if (SDLNet_ResolveHost(&ip, NULL, (Uint16)port) != 0) {
239     print_net_error("SDLNet_ResolveHost");
240     goto error;
241   }
242 
243   socket = SDLNet_TCP_Open(&ip);
244   if (!socket) {
245     print_net_error("SDLNet_TCP_Open");
246     goto error;
247   }
248 
249   r = true;
250 
251  error:
252   UnLock();
253   return r;
254 }
255 
LookForClient()256 WSocket* WSocket::LookForClient()
257 {
258   TCPsocket client_sock = SDLNet_TCP_Accept(socket);
259   if (!client_sock)
260     return NULL;
261 
262   WSocket* client = new WSocket(client_sock);
263   return client;
264 }
265 
Disconnect()266 void WSocket::Disconnect()
267 {
268   Lock();
269 
270   if (socket_set) {
271     socket_set->RemoveSocket(this);
272     if (using_tmp_socket_set) {
273       delete socket_set;
274     }
275     socket_set = NULL;
276   }
277 
278   if (socket) {
279     SDLNet_TCP_Close(socket);
280     socket = NULL;
281   }
282 
283   if (m_packet)
284     free(m_packet);
285   m_packet_size = 0;
286   m_received = 0;
287 
288   UnLock();
289 }
290 
AddToSocketSet(WSocketSet * _socket_set)291 bool WSocket::AddToSocketSet(WSocketSet* _socket_set)
292 {
293   ASSERT(socket_set == NULL);
294   int r;
295 
296   Lock();
297   socket_set = _socket_set;
298 
299   r = SDLNet_TCP_AddSocket(socket_set->socket_set, socket);
300   if (r == -1) {
301     print_net_error("SDLNet_TCP_AddSocket");
302     UnLock();
303     return false;
304   }
305 
306   UnLock();
307   return true;
308 }
309 
RemoveFromSocketSet()310 void WSocket::RemoveFromSocketSet()
311 {
312   ASSERT(!using_tmp_socket_set);
313 
314   int r;
315 
316   Lock();
317   r = SDLNet_TCP_DelSocket(socket_set->socket_set, socket);
318   if (r == -1) {
319     print_net_error("SDLNet_TCP_DelSocket");
320     ASSERT(false);
321   }
322   socket_set = NULL;
323   UnLock();
324 }
325 
AddToTmpSocketSet()326 bool WSocket::AddToTmpSocketSet()
327 {
328   ASSERT(socket_set == NULL);
329   int r;
330 
331   Lock();
332 
333   WSocketSet* tmp_socket_set = WSocketSet::GetSocketSet(1);
334   socket_set = tmp_socket_set;
335 
336   socket_set->Lock();
337   r = SDLNet_TCP_AddSocket(socket_set->socket_set, socket);
338   if (r == -1) {
339     print_net_error("SDLNet_TCP_AddSocket");
340     delete tmp_socket_set;
341     UnLock();
342     return false;
343   }
344   socket_set->sockets.push_back(this);
345   socket_set->UnLock();
346 
347   using_tmp_socket_set = true;
348   UnLock();
349 
350   return true;
351 }
352 
RemoveFromTmpSocketSet()353 void WSocket::RemoveFromTmpSocketSet()
354 {
355   ASSERT(using_tmp_socket_set);
356   int r;
357 
358   Lock();
359   socket_set->Lock();
360   r = SDLNet_TCP_DelSocket(socket_set->socket_set, socket);
361   if (r == -1) {
362     print_net_error("SDLNet_TCP_DelSocket");
363     ASSERT(false);
364   }
365   socket_set->sockets.remove(this);
366   socket_set->UnLock();
367   delete socket_set;
368   socket_set = NULL;
369 
370   using_tmp_socket_set = false;
371 
372   UnLock();
373 }
374 
Lock()375 void WSocket::Lock()
376 {
377   SDL_LockMutex(lock);
378 }
379 
UnLock()380 void WSocket::UnLock()
381 {
382   SDL_UnlockMutex(lock);
383 }
384 
385 // Static methods usefull to communicate without action
386 // (index server, handshake, ...)
387 
SendInt_NoLock(const int & nbr)388 bool WSocket::SendInt_NoLock(const int& nbr)
389 {
390   if (!IsConnected())
391     return false;
392 
393   Uint32 tmp;
394   // this is not cute, but we don't want an int -> uint conversion here
395   Uint32 u_nbr = *((const Uint32*)&nbr);
396 
397   SDLNet_Write32(u_nbr, &tmp);
398   int len = SDLNet_TCP_Send_noBlocking(socket, &tmp, 4);
399   if (len < 4) {
400     print_net_error("SDLNet_TCP_Send");
401     return false;
402   }
403 
404   return true;
405 }
406 
SendInt(const int & nbr)407 bool WSocket::SendInt(const int& nbr)
408 {
409   bool r;
410 
411   Lock();
412   r = SendInt_NoLock(nbr);
413   UnLock();
414 
415   return r;
416 }
417 
SendStr_NoLock(const std::string & str)418 bool WSocket::SendStr_NoLock(const std::string &str)
419 {
420   bool r = SendInt_NoLock(str.size());
421   if (!r)
422     return false;
423 
424   int len = SDLNet_TCP_Send_noBlocking(socket, (void*)str.c_str(), str.size());
425   if (len < int(str.size())) {
426     print_net_error("SDLNet_TCP_Send");
427     return false;
428   }
429 
430   return true;
431 }
432 
SendStr(const std::string & str)433 bool WSocket::SendStr(const std::string &str)
434 {
435   bool r;
436 
437   Lock();
438   r = SendStr_NoLock(str);
439   UnLock();
440 
441   return r;
442 }
443 
SendBuffer_NoLock(const void * data,size_t len)444 bool WSocket::SendBuffer_NoLock(const void* data, size_t len)
445 {
446   if (!IsConnected())
447     return false;
448 
449   // cast is needed to please SDL that does not use const keyword.
450   int size = SDLNet_TCP_Send_noBlocking(socket, (void*)(data), len);
451   if (size < int(len)) {
452     print_net_error("SDLNet_TCP_Send");
453     return false;
454   }
455 
456   return true;
457 }
458 
SendBuffer(const void * data,size_t len)459 bool WSocket::SendBuffer(const void* data, size_t len)
460 {
461   bool r;
462 
463   Lock();
464   r = SendBuffer_NoLock(data, len);
465   UnLock();
466 
467   return r;
468 }
469 
NbBytesAvailable(size_t & _nb_bytes)470 bool WSocket::NbBytesAvailable(size_t& _nb_bytes)
471 {
472   int nb_bytes;
473 
474   nb_bytes = SDLNet_TCP_NbBytesAvailable(socket);
475   if (nb_bytes < 0)
476     return false;
477 
478   _nb_bytes = nb_bytes;
479   return true;
480 }
481 
ReceiveBuffer_NoLock(void * data,size_t len)482 bool WSocket::ReceiveBuffer_NoLock(void* data, size_t len)
483 {
484   if (!IsConnected())
485     return false;
486 
487   int received = 0;
488 
489   while (len) {
490     // Documentation says that SDLNet_TCP_Recv receives data of *exactly* length "len" bytes
491     // from the socket "socket" into the memory pointed to by "data".
492     // BUT I have observed the contrary if len is big (len > 16384)
493     // => A loop fixes this behavior
494     //
495     //                 Gentildemon.
496 
497     received = SDLNet_TCP_Recv(socket, data, len);
498     if (received <= 0) {
499       print_net_error("SDLNet_TCP_Recv");
500       fprintf(stderr, "ERROR: SDLNet_TCP_Recv: %d\n", received);
501       return false;
502     }
503     data = (char*)data + received;
504     len -= received;
505   }
506 
507   return true;
508 }
509 
ReceiveBuffer(void * data,size_t len)510 bool WSocket::ReceiveBuffer(void* data, size_t len)
511 {
512   bool r;
513 
514   ASSERT(socket_set != NULL);
515 
516   if (!IsReady(5000)) {
517     return false;
518   }
519 
520   Lock();
521   r = ReceiveBuffer_NoLock(data, len);
522   UnLock();
523 
524   return r;
525 }
526 
ReceiveInt_NoLock(int & nbr)527 bool WSocket::ReceiveInt_NoLock(int& nbr)
528 {
529   if (!IsConnected())
530     return false;
531 
532   Uint32 tmp;
533 
534   if (!ReceiveBuffer_NoLock(&tmp, 4)) {
535     return false;
536   }
537 
538   Uint32 u_nbr = SDLNet_Read32(&tmp);
539   nbr = *((int*)&u_nbr);
540 
541   return true;
542 }
543 
ReceiveInt(int & nbr)544 bool WSocket::ReceiveInt(int& nbr)
545 {
546   bool r;
547   ASSERT(socket_set != NULL);
548 
549   if (!IsReady(5000)) {
550     return false;
551   }
552 
553   Lock();
554   r = ReceiveInt_NoLock(nbr);
555   UnLock();
556 
557   return r;
558 }
559 
ReceiveStr_NoLock(std::string & _str,size_t maxlen)560 bool WSocket::ReceiveStr_NoLock(std::string &_str, size_t maxlen)
561 {
562   bool r = true;
563   unsigned int size = 0;
564   char* str;
565 
566   r = ReceiveInt_NoLock((int&)size);
567   if (!r) {
568     goto out;
569   }
570 
571   if (size == 0) {
572     _str = "";
573     goto out;
574   }
575 
576   if (size > maxlen) {
577     r = false;
578     goto out;
579   }
580 
581   str = new char[size+1];
582   r = ReceiveBuffer_NoLock(str, size);
583   if (!r) {
584     goto out_delete;
585   }
586 
587   str[size] = '\0';
588 
589   _str = str;
590 
591  out_delete:
592   delete []str;
593  out:
594   return r;
595 }
596 
ReceiveStr(std::string & _str,size_t maxlen)597 bool WSocket::ReceiveStr(std::string &_str, size_t maxlen)
598 {
599   bool r;
600   ASSERT(socket_set != NULL);
601 
602   if (!IsReady(5000)) {
603     return false;
604   }
605 
606   Lock();
607   r = ReceiveStr_NoLock(_str, maxlen);
608   UnLock();
609 
610   return r;
611 }
612 
613 // ReceivePacket may return true with *data = NULL and len = 0
614 // That means that client is still valid BUT there are not enough data CURRENTLY
ReceivePacket(char ** data,size_t * len)615 bool WSocket::ReceivePacket(char** data, size_t* len)
616 {
617   bool r;
618 
619   Lock();
620 
621   size_t nbbytes, to_recv_now;
622   bool tested = false;
623 
624   if (m_packet_size == 0) {
625 
626     // First check there is enough data to read the size of the packet
627     r = NbBytesAvailable(nbbytes);
628     if (!r) {
629       goto error;
630     }
631 
632     // There is nodata to read but the caller has (at least must have)
633     // checked for activity. It means that the socket is disconnected.
634     if (nbbytes == 0) {
635       goto error;
636     }
637 
638     tested = true;
639 
640     // there is not enough data to read the packet size but the
641     // client is still valid
642     if (nbbytes < sizeof(uint32_t)) {
643       goto err_not_enough_data;
644     }
645 
646     // Firstly, we read the size of the incoming packet
647     int packet_len;
648     r = ReceiveInt_NoLock(packet_len);
649     if (!r) {
650       goto error;
651     }
652 
653     m_packet_size = packet_len;
654 
655     if (m_packet_size > uint32_t(MAX_VALID_PACKET_SIZE)) {
656       fprintf(stderr, "ERROR: network packet is too big: %u bytes. (max: %u)\n",
657         m_packet_size, MAX_VALID_PACKET_SIZE);
658       goto error;
659     }
660 
661     m_packet = (char*)malloc(m_packet_size);
662     if (!m_packet) {
663       fprintf(stderr, "ERROR: memory allocation failed (%u bytes)\n", m_packet_size);
664       goto error;
665     }
666 
667     SDLNet_Write32(m_packet_size, m_packet);
668     m_received = sizeof(uint32_t);
669   }
670 
671   // Check if the data are already there
672   r = NbBytesAvailable(nbbytes);
673   if (!r) {
674     goto error;
675   }
676 
677   // There is nodata to read but the caller has (at least must have)
678   // checked for activity. It means that the socket is disconnected.
679   if (!tested && nbbytes == 0) {
680     goto error;
681   }
682 
683   // Compute how many data we have to receive now
684   to_recv_now = m_packet_size - m_received;
685   if (to_recv_now > nbbytes) {
686     to_recv_now = nbbytes;
687   }
688 
689   r = ReceiveBuffer_NoLock(m_packet + m_received, to_recv_now);
690   if (!r) {
691     fprintf(stderr, "ERROR: fail to receive data\n");
692     goto error;
693   }
694 
695   m_received += to_recv_now;
696   nbbytes -= to_recv_now;
697 
698   // the packet can not have been read in one time
699   // but client is still valid
700   if (m_received != m_packet_size) {
701     goto err_not_enough_data;
702   }
703 
704   *data = m_packet;
705   *len = m_packet_size;
706 
707  out_finished:
708   m_packet = NULL;
709   m_packet_size = 0;
710   m_received = 0;
711 
712   UnLock();
713   return r;
714 
715  error:
716   r = false;
717   if (m_packet)
718     free(m_packet);
719 
720   goto out_finished;
721 
722  err_not_enough_data:
723   *data = NULL;
724   *len = 0;
725   r = true;
726   UnLock();
727 
728   return true;
729 }
730 
IsReady(int timeout,bool force_check_activity) const731 bool WSocket::IsReady(int timeout, bool force_check_activity) const
732 {
733   if (socket == NULL)
734     return false;
735 
736   if (timeout != 0 || force_check_activity) {
737     ASSERT(socket_set != NULL);
738     int sockets_ready = socket_set->CheckActivity(timeout);
739 
740     if (sockets_ready == -1) {
741       print_net_error("SDLNet_CheckSockets");
742       return false;
743     }
744 
745     if (sockets_ready == 0)
746       return false;
747   }
748 
749   return SDLNet_SocketReady(socket);
750 }
751 
IsReady(int timeout) const752 bool WSocket::IsReady(int timeout) const
753 {
754   return IsReady(timeout, false);
755 }
756 
GetAddress()757 const std::string WSocket::GetAddress()
758 {
759   ASSERT(socket != NULL);
760   // Resolve the address only once,
761   // as this method get called by some debug logging code quite often.
762   if (!address_field_valid) {
763     IPaddress* ip = SDLNet_TCP_GetPeerAddress(socket);
764     address = SDLNet_TryToResolveIP(ip);
765     address_field_valid = true;
766   }
767   return address;
768 }
769