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  * Network layer for warmux.
20  *****************************************************************************/
21 
22 #include <sys/types.h>
23 #include <iostream>
24 
25 // Standard header, only needed for the following method
26 #ifdef WIN32
27 #  include <winsock2.h>
28 #  include <ws2tcpip.h>
29 #else
30 #  ifdef GEKKO
31 #    include <ogcsys.h>
32 #    include <network.h>
33 #    define socket  net_socket
34 #    define bind    net_bind
35 #    define connect net_connect
36 #    define setsockopt net_setsockopt
37 #  else
38 #    include <sys/socket.h>
39 #    include <netdb.h>
40 #    include <netinet/in.h>
41 #    include <arpa/nameser.h>
42 #    include <resolv.h>
43 #  endif
44 #  include <errno.h>
45 #  include <unistd.h>
46 #endif
47 
48 //-----------------------------------------------------------------------------
49 #include <SDL_thread.h>
50 #include <SDL_timer.h>
51 #include <WARMUX_error.h>
52 #include <WARMUX_i18n.h>
53 #include <WARMUX_index_svr_msg.h>
54 #include <WARMUX_network.h>
55 #include <WARMUX_socket.h>
56 #include "extSDL_net.h"
57 
58 #ifdef LOG_NETWORK
59 #  include <sys/stat.h>
60 #  include <fcntl.h>
61 #  ifdef WIN32
62 #    include <io.h>
63 #  endif
64 #endif
65 //-----------------------------------------------------------------------------
66 
67 static const std::string WARMUX_VERSION = PACKAGE_VERSION;
68 
69 bool WNet::sdlnet_initialized = false;
70 
71 //-----------------------------------------------------------------------------
72 
Init()73 void WNet::Init()
74 {
75   if (sdlnet_initialized) {
76     return;
77   }
78 
79   if (SDLNet_Init()) {
80     Error("Failed to initialize network library! (SDL_Net)");
81     exit(1);
82   }
83   sdlnet_initialized = true;
84 
85   std::cout << "o " << _("Network initialization") << std::endl;
86 }
87 
Quit()88 void WNet::Quit()
89 {
90   if (!sdlnet_initialized) {
91     return;
92   }
93 
94   SDLNet_Quit();
95   sdlnet_initialized = false;
96 }
97 
98 
99 //-----------------------------------------------------------------------------
100 
101 #ifdef WIN32
102 # define SOCKET_PARAM    char
103 #else
104 typedef int SOCKET;
105 # define SOCKET_PARAM    void
106 # ifndef INVALID_SOCKET
107 #   define INVALID_SOCKET  (-1)
108 # endif
109 # ifndef SOCKET_ERROR
110 #   define SOCKET_ERROR    (-1)
111 # endif
112 # define closesocket(fd) close(fd)
113 #endif
114 
115 #ifndef AI_NUMERICSERV
116 #define AI_NUMERICSERV 0
117 #endif
118 
119 // static method
GetError()120 connection_state_t WNet::GetError()
121 {
122 #ifdef WIN32
123   int code = WSAGetLastError();
124   switch (code)
125   {
126   case WSAECONNREFUSED: return CONN_REJECTED;
127   case WSAEINPROGRESS:
128   case WSAETIMEDOUT: return CONN_TIMEOUT;
129   default:
130     fprintf(stderr, "Generic network error of code %i\n", code);
131     return CONN_BAD_SOCKET;
132   }
133 #else
134   switch(errno)
135   {
136   case ECONNREFUSED: return CONN_REJECTED;
137   case EINPROGRESS:
138   case ETIMEDOUT: return CONN_TIMEOUT;
139   default:
140     fprintf(stderr, "Generic network error of code %i\n", errno);
141     return CONN_BAD_SOCKET;
142   }
143 #endif
144 }
145 
CheckHost(const std::string & host,int prt)146 connection_state_t WNet::CheckHost(const std::string &host, int prt)
147 {
148 #ifdef GEKKO
149   return CONN_BAD_SOCKET;
150 #else
151   connection_state_t s = CONN_BAD_SOCKET;
152   int r;
153   SOCKET sfd;
154   char port[10];
155   struct addrinfo hints;
156   struct addrinfo *result, *rp;
157 
158   memset(&hints, 0, sizeof(struct addrinfo));
159   hints.ai_family = AF_UNSPEC;      /* Allow IPv4 or IPv6 */
160   hints.ai_socktype = SOCK_STREAM;  /* TCP protocol only */
161   hints.ai_flags =  AI_NUMERICSERV; /* Service is a numeric port number */
162   hints.ai_protocol = IPPROTO_TCP;
163 
164   snprintf(port, 10, "%d", prt);
165 
166   r = getaddrinfo(host.c_str(), port, &hints, &result);
167   if (r != 0) {
168 
169     fprintf(stderr, "getaddrinfo returns %d\n", r);
170 
171     const char * gai_errmsg = gai_strerror(r);
172     ASSERT(gai_errmsg);
173     fprintf(stderr, "%s\n", gai_errmsg);
174 
175     if (r == EAI_NONAME) {
176       s = CONN_BAD_HOST;
177       goto err_addrinfo;
178     }
179 
180     s = CONN_BAD_SOCKET;
181     goto err_addrinfo;
182   }
183 
184   /* getaddrinfo() returns a list of address structures.
185      Try each address until we successfully connect(2).
186      If socket(2) (or connect(2)) fails, we (close the socket
187      and) try the next address. */
188   for (rp = result; rp != NULL; rp = rp->ai_next) {
189 
190     sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
191 
192     if (sfd == INVALID_SOCKET)
193       continue;
194 
195     // Try to set the timeout
196 #ifdef WIN32
197     int timeout = 5000; // ms
198 #else
199     struct timeval timeout;
200     memset(&timeout, 0, sizeof(timeout));
201     timeout.tv_sec = 5; // 5seconds timeout
202 #endif
203     if (setsockopt(sfd, SOL_SOCKET, SO_RCVTIMEO, (SOCKET_PARAM*)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
204       fprintf(stderr, "Setting receive timeout on socket failed\n");
205       closesocket(sfd);
206       continue;
207     }
208 
209     if (setsockopt(sfd, SOL_SOCKET, SO_SNDTIMEO, (SOCKET_PARAM*)&timeout, sizeof(timeout)) == SOCKET_ERROR) {
210       fprintf(stderr, "Setting send timeout on socket failed\n");
211       closesocket(sfd);
212       continue;
213     }
214 
215     int err = connect(sfd, rp->ai_addr, rp->ai_addrlen);
216     if (err != -1)
217       break; /* Success */
218 
219     /* Set state to last error found */
220     s = WNet::GetError();
221     closesocket(sfd);
222   }
223 
224   if (rp == NULL) { /* No address succeeded */
225     fprintf(stderr, "Could not connect\n");
226     goto error;
227   }
228 
229   closesocket(sfd);
230   s = CONNECTED;
231 
232  error:
233   freeaddrinfo(result); /* No longer needed */
234 
235  err_addrinfo:
236   return s;
237 #endif
238 }
239 
IPtoDNS(IPaddress * ip)240 std::string WNet::IPtoDNS(IPaddress *ip)
241 {
242   return SDLNet_TryToResolveIP(ip);
243 }
244 
IPStrToDNS(const std::string & host)245 std::string WNet::IPStrToDNS(const std::string& host)
246 {
247   IPaddress ip;
248   int r;
249   r = SDLNet_ResolveHost(&ip, host.c_str(), 8080);
250   if (r)
251     return host;
252 
253   return IPtoDNS(&ip);
254 }
255 
256 //-----------------------------------------------------------------------------
257 //-----------------------------------------------------------------------------
258 
Batch(void * buffer,const int & nbr)259 uint WNet::Batch(void* buffer, const int& nbr)
260 {
261   ASSERT(sdlnet_initialized);
262 
263   // this is not cute, but we don't want an int -> uint conversion here
264   Uint32 u_nbr = *((const Uint32*)&nbr);
265 
266   SDLNet_Write32(u_nbr, buffer);
267 
268   return sizeof(u_nbr);
269 }
270 
Batch(void * buffer,const std::string & str)271 unsigned int WNet::Batch(void* buffer, const std::string &str)
272 {
273   uint size = str.size();
274   Batch(buffer, size);
275   memcpy(((char*)buffer)+sizeof(Uint32), str.c_str(), size);
276   return sizeof(Uint32)+size;
277 }
278 
FinalizeBatch(void * data,size_t len)279 void WNet::FinalizeBatch(void* data, size_t len)
280 {
281   SDLNet_Write32(len, (void*)( ((char*)data)+4 ) );
282 }
283 
284 //-----------------------------------------------------------------------------
285 //-----------------------------------------------------------------------------
286 
Server_HandShake(WSocket & client_socket,const std::string & game_name,const std::string & password,std::string & nickname,uint client_player_id,bool client_will_be_master)287 bool WNet::Server_HandShake(WSocket& client_socket,
288                             const std::string& game_name,
289                             const std::string& password,
290                             std::string& nickname,
291                             uint client_player_id,
292                             bool client_will_be_master)
293 {
294   bool ret = false;
295   std::string version;
296   std::string _password;
297 
298   // Adding the socket to a temporary socket set
299   if (!client_socket.AddToTmpSocketSet())
300     goto error_no_socket_set;
301 
302   // 1) Receive the version number
303   if (!client_socket.ReceiveStr(version, 40))
304     goto error;
305 
306   if (!client_socket.SendStr(WARMUX_VERSION))
307     goto error;
308 
309   if (WARMUX_VERSION != version) {
310     std::cerr << "Client disconnected: wrong version " << version.c_str()
311              << std::endl;
312     goto error;
313   }
314 
315   // 2) Check the password
316   if (!client_socket.ReceiveStr(_password, 100))
317     goto error;
318 
319   if (_password != password) {
320     std::cerr << "Client disconnected: wrong password " << _password.c_str()
321               << std::endl;
322     client_socket.SendInt(1);
323     goto error;
324   }
325 
326   // Server: password OK
327   if (!client_socket.SendInt(0))
328     goto error;
329 
330   // 3) Server: client will be master ?
331   if (!client_socket.SendInt(client_will_be_master))
332     goto error;
333 
334   // 4) Get nickname of the client
335   if (!client_socket.ReceiveStr(nickname, 100))
336     goto error;
337 
338   // 5) Send gamename to the client
339   if (!client_socket.SendStr(game_name))
340     goto error;
341 
342   // 6) Send its player id to the client
343   if (!client_socket.SendInt(client_player_id))
344     goto error;
345 
346   // Server: Handshake done successfully :)
347   ret = true;
348 
349  error:
350   client_socket.RemoveFromTmpSocketSet();
351  error_no_socket_set:
352   if (!ret) {
353     std::cerr << "Server: HandShake with client has failed!" << std::endl;
354   }
355   return ret;
356 }
357 
GetGameStateAsString(net_game_state_t state)358 const char * WNet::GetGameStateAsString(net_game_state_t state)
359 {
360   switch (state) {
361     case NO_NETWORK:
362       return "NO_NETWORK";
363     case NETWORK_MENU_INIT:
364       return "NETWORK_MENU_INIT";
365     case NETWORK_MENU_OK:
366       return "NETWORK_MENU_OK";
367     case NETWORK_LOADING_DATA:
368       return "NETWORK_LOADING_DATA";
369     case NETWORK_READY_TO_PLAY:
370       return "READY_TO_PLAY";
371     case NETWORK_PLAYING:
372       return "NETWORK_PLAYING";
373     case NETWORK_NEXT_GAME:
374       return "NETWORK_NEXT_GAME";
375     default:
376       return "?";
377   }
378 }
379