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