Atrinik Client  4.0
socket.c
Go to the documentation of this file.
1 /*************************************************************************
2  * Atrinik, a Multiplayer Online Role Playing Game *
3  * *
4  * Copyright (C) 2009-2014 Alex Tokar and Atrinik Development Team *
5  * *
6  * Fork from Crossfire (Multiplayer game for X-windows). *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. *
21  * *
22  * The author can be reached at admin@atrinik.org *
23  ************************************************************************/
24 
30 #include <global.h>
31 #include <toolkit/packet.h>
32 #include <network_graph.h>
33 #include <toolkit/socket_crypto.h>
34 
35 static SDL_Thread *input_thread;
36 static SDL_mutex *input_buffer_mutex;
37 static SDL_cond *input_buffer_cond;
38 
39 static SDL_Thread *output_thread;
40 static SDL_mutex *output_buffer_mutex;
41 static SDL_cond *output_buffer_cond;
42 
46 static SDL_mutex *socket_mutex;
47 
51 static int abort_thread = 0;
52 
53 /* start is the first waiting item in queue, end is the most recent enqueued */
54 static command_buffer *input_queue_start = NULL, *input_queue_end = NULL;
55 static command_buffer *output_queue_start = NULL, *output_queue_end = NULL;
56 
68 command_buffer *command_buffer_new(size_t len, uint8_t *data)
69 {
70  command_buffer *buf = emalloc(sizeof(command_buffer) + len + 1);
71 
72  buf->next = buf->prev = NULL;
73  buf->len = len;
74 
75  if (data) {
76  memcpy(buf->data, data, len);
77  }
78 
79  buf->data[len] = '\0';
80  return buf;
81 }
82 
89 {
90  efree(buf);
91 }
92 
96 static void command_buffer_enqueue(command_buffer *buf, command_buffer **queue_start, command_buffer **queue_end)
97 {
98  buf->next = NULL;
99  buf->prev = *queue_end;
100 
101  if (*queue_start == NULL) {
102  *queue_start = buf;
103  }
104 
105  if (buf->prev) {
106  buf->prev->next = buf;
107  }
108 
109  *queue_end = buf;
110 }
111 
115 static void command_buffer_enqueue_first(command_buffer *buf, command_buffer **queue_start, command_buffer **queue_end)
116 {
117  buf->next = *queue_start;
118  buf->prev = NULL;
119 
120  if (*queue_end == NULL) {
121  *queue_end = buf;
122  }
123 
124  if (buf->next) {
125  buf->next->prev = buf;
126  }
127 
128  *queue_start = buf;
129 }
130 
135 {
136  command_buffer *buf = *queue_start;
137 
138  if (buf) {
139  *queue_start = buf->next;
140 
141  if (buf->next) {
142  buf->next->prev = NULL;
143  } else {
144  *queue_end = NULL;
145  }
146  }
147 
148  return buf;
149 }
150 
151 void socket_send_packet(struct packet_struct *packet)
152 {
153  HARD_ASSERT(packet != NULL);
154 
155  if (csocket.sc == NULL) {
156  packet_free(packet);
157  return;
158  }
159 
160  packet_struct *packet_meta = packet_new(0, 4, 0);
161  if (socket_is_secure(csocket.sc)) {
162  bool checksum_only = !socket_crypto_client_should_encrypt(packet->type);
163  packet = socket_crypto_encrypt(csocket.sc,
164  packet,
165  packet_meta,
166  checksum_only);
167  if (packet == NULL) {
168  /* Logging already done. */
169  cpl.state = ST_START;
170  return;
171  }
172  } else {
173  packet_append_uint16(packet_meta, packet->len + 1);
174  packet_append_uint8(packet_meta, packet->type);
175  }
176 
177  command_buffer *buf1 = command_buffer_new(packet_meta->len,
178  packet_meta->data);
179  packet_free(packet_meta);
180  command_buffer *buf2 = command_buffer_new(packet->len,
181  packet->data);
182  packet_free(packet);
183 
184  SDL_LockMutex(output_buffer_mutex);
185  command_buffer_enqueue(buf1, &output_queue_start, &output_queue_end);
186  command_buffer_enqueue(buf2, &output_queue_start, &output_queue_end);
187  SDL_CondSignal(output_buffer_cond);
188  SDL_UnlockMutex(output_buffer_mutex);
189 }
190 
198 {
199  command_buffer *buf;
200 
201  SDL_LockMutex(input_buffer_mutex);
202  buf = command_buffer_dequeue(&input_queue_start, &input_queue_end);
203  SDL_UnlockMutex(input_buffer_mutex);
204  return buf;
205 }
206 
207 void add_input_command(command_buffer *buf)
208 {
209  SDL_LockMutex(input_buffer_mutex);
210  command_buffer_enqueue_first(buf, &input_queue_start, &input_queue_end);
211  SDL_CondSignal(input_buffer_cond);
212  SDL_UnlockMutex(input_buffer_mutex);
213 }
214 
215 static int reader_thread_loop(void *dummy)
216 {
217  static uint8_t *readbuf = NULL;
218  static int readbuf_size = 256;
219  int readbuf_len = 0;
220  int header_len = 0;
221  int cmd_len = -1;
222 
223  if (!readbuf) {
224  readbuf = emalloc(readbuf_size);
225  }
226 
227  while (!abort_thread) {
228  int toread;
229 
230  /* First, try to read a command length sequence */
231  if (readbuf_len < 2) {
232  /* Three-byte length? */
233  if (readbuf_len > 0 && (readbuf[0] & 0x80)) {
234  toread = 3 - readbuf_len;
235  } else {
236  toread = 2 - readbuf_len;
237  }
238  } else if (readbuf_len == 2 && (readbuf[0] & 0x80)) {
239  toread = 1;
240  } else {
241  /* If we have a finished header, get the packet size from it. */
242  if (readbuf_len <= 3) {
243  uint8_t *p = readbuf;
244 
245  header_len = (*p & 0x80) ? 3 : 2;
246  cmd_len = 0;
247 
248  if (header_len == 3) {
249  cmd_len += ((int) (*p++) & 0x7f) << 16;
250  }
251 
252  cmd_len += ((int) (*p++)) << 8;
253  cmd_len += ((int) (*p++));
254  }
255 
256  toread = cmd_len + header_len - readbuf_len;
257 
258  if (readbuf_len + toread > readbuf_size) {
259  uint8_t *tmp = readbuf;
260 
261  readbuf_size = readbuf_len + toread;
262  readbuf = emalloc(readbuf_size);
263  memcpy(readbuf, tmp, readbuf_len);
264  efree(tmp);
265  }
266  }
267 
268  size_t amt;
269  bool success = socket_read(csocket.sc, (void *) (readbuf + readbuf_len),
270  toread, &amt);
271  if (!success) {
272  break;
273  }
274 
275  readbuf_len += amt;
277  amt);
278 
279  /* Finished with a command? */
280  if (readbuf_len == cmd_len + header_len && !abort_thread) {
281  command_buffer *buf = command_buffer_new(readbuf_len - header_len,
282  readbuf + header_len);
283 
284  SDL_LockMutex(input_buffer_mutex);
285  command_buffer_enqueue(buf, &input_queue_start, &input_queue_end);
286  SDL_CondSignal(input_buffer_cond);
287  SDL_UnlockMutex(input_buffer_mutex);
288 
289  cmd_len = -1;
290  header_len = 0;
291  readbuf_len = 0;
292  }
293  }
294 
296 
297  if (readbuf != NULL) {
298  efree(readbuf);
299  readbuf = NULL;
300  }
301 
302  return -1;
303 }
304 
312 static int writer_thread_loop(void *dummy)
313 {
314  command_buffer *buf = NULL;
315 
316  while (!abort_thread) {
317  SDL_LockMutex(output_buffer_mutex);
318 
319  while (output_queue_start == NULL && !abort_thread) {
320  SDL_CondWait(output_buffer_cond, output_buffer_mutex);
321  }
322 
323  buf = command_buffer_dequeue(&output_queue_start, &output_queue_end);
324  SDL_UnlockMutex(output_buffer_mutex);
325  size_t written = 0;
326 
327  while (buf != NULL && written < buf->len && !abort_thread) {
328  size_t amt;
329  bool success = socket_write(csocket.sc, (const void *) (buf->data +
330  written), buf->len - written, &amt);
331  if (!success) {
332  break;
333  }
334 
335  written += amt;
338  }
339 
340  if (buf != NULL) {
341  command_buffer_free(buf);
342  buf = NULL;
343  }
344  }
345 
347  return 0;
348 }
349 
354 {
355  if (input_buffer_cond == NULL) {
356  input_buffer_cond = SDL_CreateCond();
357  input_buffer_mutex = SDL_CreateMutex();
358  output_buffer_cond = SDL_CreateCond();
359  output_buffer_mutex = SDL_CreateMutex();
360  socket_mutex = SDL_CreateMutex();
361  }
362 
363  abort_thread = 0;
364 
365  input_thread = SDL_CreateThread(reader_thread_loop, NULL);
366 
367  if (input_thread == NULL) {
368  LOG(ERROR, "Unable to start socket thread: %s", SDL_GetError());
369  exit(1);
370  }
371 
372  output_thread = SDL_CreateThread(writer_thread_loop, NULL);
373 
374  if (output_thread == NULL) {
375  LOG(ERROR, "Unable to start socket thread: %s", SDL_GetError());
376  exit(1);
377  }
378 }
379 
386 {
388 
389  SDL_WaitThread(output_thread, NULL);
390  SDL_WaitThread(input_thread, NULL);
391 
392  input_thread = output_thread = NULL;
393 }
394 
403 {
404  if (abort_thread) {
406  abort_thread = 0;
407 
408  /* Empty all queues */
409  while (input_queue_start) {
410  command_buffer_free(command_buffer_dequeue(&input_queue_start, &input_queue_end));
411  }
412 
413  while (output_queue_start) {
414  command_buffer_free(command_buffer_dequeue(&output_queue_start, &output_queue_end));
415  }
416 
417  LOG(INFO, "Connection lost.");
418  return 1;
419  }
420 
421  return 0;
422 }
423 
430 {
431  HARD_ASSERT(csock != NULL);
432 
433  SDL_LockMutex(socket_mutex);
434 
435  if (csock->sc != NULL) {
436  socket_destroy(csock->sc);
437  csock->sc = NULL;
438  }
439 
440  abort_thread = 1;
441 
442  /* Poke anyone waiting at a cond */
443  SDL_CondSignal(input_buffer_cond);
444  SDL_CondSignal(output_buffer_cond);
445 
446  SDL_UnlockMutex(socket_mutex);
447 }
448 
453 {
454  if (csocket.sc != NULL) {
456  }
457 
458 #ifdef WIN32
459  WSACleanup();
460 #endif
461 }
462 
476 bool
478  const char *host,
479  int port,
480  bool secure)
481 {
482  HARD_ASSERT(csock != NULL);
483  HARD_ASSERT(host != NULL);
484 
485  csock->sc = socket_create(host, port, secure, SOCKET_ROLE_CLIENT, false);
486  if (csock->sc == NULL) {
487  return false;
488  }
489 
490  if (!socket_connect(csock->sc)) {
491  goto error;
492  }
493 
494  if (!socket_opt_linger(csock->sc, true, 5)) {
495  goto error;
496  }
497 
499  if (!socket_opt_ndelay(csock->sc, true)) {
500  goto error;
501  }
502  }
503 
504  if (!socket_opt_recv_buffer(csock->sc, 65535)) {
505  goto error;
506  }
507 
508  return true;
509 
510 error:
511  socket_destroy(csock->sc);
512  csock->sc = NULL;
513  return false;
514 }
static int abort_thread
Definition: socket.c:51
void client_socket_close(client_socket_t *csock)
Definition: socket.c:429
Definition: main.h:252
void socket_thread_stop(void)
Definition: socket.c:385
void network_graph_update(int type, int traffic, size_t bytes)
uint8_t data[1]
Definition: client.h:83
static void command_buffer_enqueue(command_buffer *buf, command_buffer **queue_start, command_buffer **queue_end)
Definition: socket.c:96
bool client_socket_open(client_socket_t *csock, const char *host, int port, bool secure)
Definition: socket.c:477
size_t len
Definition: client.h:80
static SDL_mutex * socket_mutex
Definition: socket.c:46
static void command_buffer_enqueue_first(command_buffer *buf, command_buffer **queue_start, command_buffer **queue_end)
Definition: socket.c:115
Incoming traffic.
Definition: network_graph.h:43
client_socket_t csocket
Definition: main.c:51
Client_Player cpl
Definition: client.c:50
Outgoing traffic.
Definition: network_graph.h:44
void command_buffer_free(command_buffer *buf)
Definition: socket.c:88
int64_t setting_get_int(int cat, int setting)
Definition: settings.c:414
static int writer_thread_loop(void *dummy)
Definition: socket.c:312
command_buffer * get_next_input_command(void)
Definition: socket.c:197
command_buffer * command_buffer_new(size_t len, uint8_t *data)
Definition: socket.c:68
void socket_thread_start(void)
Definition: socket.c:353
struct command_buffer * next
Definition: client.h:74
int handle_socket_shutdown(void)
Definition: socket.c:402
static command_buffer * command_buffer_dequeue(command_buffer **queue_start, command_buffer **queue_end)
Definition: socket.c:134
void client_socket_deinitialize(void)
Definition: socket.c:452
struct command_buffer * prev
Definition: client.h:77