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