Crossfire Server, Trunk
lowlevel.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
26 #include "global.h"
27 
28 #include <assert.h>
29 #include <errno.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 
34 #include "shared/newclient.h"
35 #include "sproto.h"
36 
37 #ifdef WIN32
38 #include <winsock2.h>
39 #endif
40 
41 /***********************************************************************
42  *
43  * SockList functions/utilities
44  *
45  **********************************************************************/
46 
53  SockList_Reset(sl);
54 }
55 
63  (void)sl;
64 }
65 
72  sl->len = 2;
73 }
74 
81  sl->len = 0;
82 }
83 
92 static void SockList_Ensure(const SockList *sl, size_t size) {
93  if (sl->len+size > sizeof(sl->buf)) {
95  }
96 }
97 
103 void SockList_AddChar(SockList *sl, unsigned char data) {
104  SockList_Ensure(sl, 1);
105  sl->buf[sl->len++] = data;
106 }
107 
113 void SockList_AddShort(SockList *sl, uint16_t data) {
114  SockList_Ensure(sl, 2);
115  sl->buf[sl->len++] = (data>>8)&0xff;
116  sl->buf[sl->len++] = data&0xff;
117 }
118 
124 void SockList_AddInt(SockList *sl, uint32_t data) {
125  SockList_Ensure(sl, 4);
126  sl->buf[sl->len++] = (data>>24)&0xff;
127  sl->buf[sl->len++] = (data>>16)&0xff;
128  sl->buf[sl->len++] = (data>>8)&0xff;
129  sl->buf[sl->len++] = data&0xff;
130 }
131 
137 void SockList_AddInt64(SockList *sl, uint64_t data) {
138  SockList_Ensure(sl, 8);
139  sl->buf[sl->len++] = (char)((data>>56)&0xff);
140  sl->buf[sl->len++] = (char)((data>>48)&0xff);
141  sl->buf[sl->len++] = (char)((data>>40)&0xff);
142  sl->buf[sl->len++] = (char)((data>>32)&0xff);
143  sl->buf[sl->len++] = (char)((data>>24)&0xff);
144  sl->buf[sl->len++] = (char)((data>>16)&0xff);
145  sl->buf[sl->len++] = (char)((data>>8)&0xff);
146  sl->buf[sl->len++] = (char)(data&0xff);
147 }
148 
154 void SockList_AddString(SockList *sl, const char *data) {
155  SockList_AddData(sl, data, strlen(data));
156 }
157 
164 void SockList_AddData(SockList *sl, const void *data, size_t len) {
165  SockList_Ensure(sl, len);
166  memcpy(sl->buf+sl->len, data, len);
167  sl->len += len;
168 }
169 
176 void SockList_AddLen8Data(SockList *sl, const void *data, size_t len) {
177  assert(len <= 255);
178  SockList_AddChar(sl, len);
179  SockList_AddData(sl, data, len);
180 }
181 
188 void SockList_AddLen16Data(SockList *sl, const void *data, size_t len) {
189  assert(len <= 65535);
190  SockList_AddShort(sl, len);
191  SockList_AddData(sl, data, len);
192 }
193 
199 void SockList_AddPrintf(SockList *sl, const char *format, ...) {
200  size_t size;
201  int n;
202  va_list arg;
203 
204  size = sizeof(sl->buf)-sl->len;
205 
206  va_start(arg, format);
207  n = vsnprintf((char *)sl->buf+sl->len, size, format, arg);
208  va_end(arg);
209 
210  if (n <= -1 || (size_t)n >= size) {
212  }
213  sl->len += (size_t)n;
214 }
215 
223  char *p;
224 
225  p = stringbuffer_finish(sb);
226  SockList_AddString(sl, p);
227  free(p);
228 }
229 
235  SockList_Ensure(sl, 1);
236  sl->buf[sl->len] = '\0';
237 }
238 
243 size_t SockList_Avail(const SockList *sl) {
244  return sizeof(sl->buf)-sl->len;
245 }
246 
251 int GetInt_String(const unsigned char *data) {
252  return ((data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]);
253 }
254 
255 short GetShort_String(const unsigned char *data) {
256  return ((data[0]<<8)+data[1]);
257 }
258 
259 /******************************************************************************
260  *
261  * Start of read routines.
262  *
263  ******************************************************************************/
264 
272 int SockList_ReadPacket(int fd, SockList *sl, int len) {
273  int stat, toread;
274 
275  /* We already have a partial packet */
276  if (sl->len < 2) {
277 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: change read() to recv() */
278 
279  stat = recv(fd, sl->buf+sl->len, 2-sl->len, 0);
280 
281 #else
282  do {
283  stat = read(fd, sl->buf+sl->len, 2-sl->len);
284  } while ((stat == -1) && (errno == EINTR));
285 #endif
286  if (stat < 0) {
287  /* In non blocking mode, EAGAIN is set when there is no
288  * data available.
289  */
290 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: error handling for win32 */
291  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
292  if (WSAGetLastError() == WSAECONNRESET)
293  LOG(llevDebug, "Connection closed by client\n");
294  else {
295  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
296  }
297  return -1; /* kick this user! */
298  }
299 #else
300  if (errno == ECONNRESET) {
301  LOG(llevDebug, "ReadPacket got error %s, returning -1\n", strerror(errno));
302  return -1;
303  }
304  if (errno != EAGAIN && errno != EWOULDBLOCK) {
305  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
306  }
307 #endif
308  return 0; /*Error */
309  }
310  if (stat == 0)
311  return -1;
312  sl->len += stat;
313 #ifdef CS_LOGSTATS
314  cst_tot.ibytes += stat;
315  cst_lst.ibytes += stat;
316 #endif
317  if (stat < 2)
318  return 0; /* Still don't have a full packet */
319  }
320  /* Figure out how much more data we need to read. Add 2 from the
321  * end of this - size header information is not included.
322  */
323  toread = 2+(sl->buf[0]<<8)+sl->buf[1]-sl->len;
324  if ((toread+(int)sl->len) >= len) {
325  LOG(llevError, "SockList_ReadPacket: Want to read more bytes than will fit in buffer (%lu>=%lu).\n", (unsigned long)toread+sl->len, (unsigned long)len);
326  /* Quick hack in case for 'oldsocketmode' input. If we are
327  * closing the socket anyways, then reading this extra 100 bytes
328  * shouldn't hurt.
329  */
330 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
331  stat = recv(fd, sl->buf+2, 100, 0);
332 #else
333  stat = read(fd, sl->buf+2, 100);
334 #endif /* end win32 */
335  (void) stat; // Don't care how much we read; avoid complier warnings
336 
337  /* return error so the socket is closed */
338  return -1;
339  }
340  do {
341 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
342  stat = recv(fd, sl->buf+sl->len, toread, 0);
343 #else
344  do {
345  stat = read(fd, sl->buf+sl->len, toread);
346  } while ((stat < 0) && (errno == EINTR));
347 #endif
348  if (stat < 0) {
349 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change error handling for win32 */
350  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
351  if (WSAGetLastError() == WSAECONNRESET)
352  LOG(llevDebug, "Connection closed by client\n");
353  else {
354  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
355  }
356  return -1; /* kick this user! */
357  }
358 #else
359  if (errno != EAGAIN && errno != EWOULDBLOCK) {
360  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
361  }
362 #endif
363  return 0; /*Error */
364  }
365  if (stat == 0)
366  return -1;
367  sl->len += stat;
368 #ifdef CS_LOGSTATS
369  cst_tot.ibytes += stat;
370  cst_lst.ibytes += stat;
371 #endif
372  toread -= stat;
373  if (toread == 0)
374  return 1;
375  if (toread < 0) {
376  LOG(llevError, "SockList_ReadPacket: Read more bytes than desired.\n");
377  return 1;
378  }
379  } while (toread > 0);
380  return 0;
381 }
382 
383 /*******************************************************************************
384  *
385  * Start of write related routines.
386  *
387  ******************************************************************************/
388 
396 static void Write_To_Socket(socket_struct* ns, const unsigned char* buf, const int len) {
397  if (ns->status == Ns_Dead || !buf) {
398  LOG(llevDebug, "Write_To_Socket called with dead socket\n");
399  return;
400  }
401 
402  const int amt = send(ns->fd, buf, len, 0);
403  if (amt < 0) { /* We got an error */
404 #ifdef WIN32 /* ***win32 Write_To_Socket: change error handling */
405  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
406  LOG(llevInfo, "New socket write failed WTS (%d).\n",
407  WSAGetLastError());
408 #else
409  if (errno != EWOULDBLOCK) {
410  LOG(llevInfo, "New socket write failed WTS: %s\n",
411  strerror(errno));
412 #endif
413  ns->status = Ns_Dead;
414  return;
415  } else { /* EWOULDBLOCK */
416  LOG(llevError,
417  "Write_To_Socket: write would block; disconnecting. Try "
418  "increasing SOCKETBUFSIZE.\n");
419  ns->status = Ns_Dead;
420  return;
421  }
422  } else if (amt != len) {
423  LOG(llevError, "Write_To_Socket: write wrote less than requested; "
424  "disconnecting. Try increasing SOCKETBUFSIZE.\n");
425  ns->status = Ns_Dead;
426  return;
427  }
428 #ifdef CS_LOGSTATS
429  cst_tot.obytes += amt;
430  cst_lst.obytes += amt;
431 #endif
432 }
433 
441  if (ns->status == Ns_Dead || sl == NULL)
442  return;
443 
444  sl->buf[0] = ((sl->len-2)>>8)&0xFF;
445  sl->buf[1] = (sl->len-2)&0xFF;
446  Write_To_Socket(ns, sl->buf, sl->len);
447 }
448 
449 /******************************************************************************
450  *
451  * statistics logging functions.
452  *
453  ******************************************************************************/
454 
455 #ifdef CS_LOGSTATS
456 /* cst_tot is for the life of the server, cst_last is for the last series of
457  * stats
458  */
460 
464 void write_cs_stats(void) {
465  time_t now = time(NULL);
466 
467  /* If no connections recently, don't bother to log anything */
468  if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
469  return;
470 
471  LOG(llevInfo, "STAT: players: %d active, %d total\n", count_players(), count_all_players());
472  /* CSSTAT is put in so scripts can easily find the line */
473  LOG(llevInfo, "CSSTAT: %.16s tot %d %d %d %ld inc %d %d %d %ld\n",
474  ctime(&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
476  cst_lst.max_conn, (long)(now-cst_lst.time_start));
477  cst_lst.ibytes = 0;
478  cst_lst.obytes = 0;
480  cst_lst.time_start = now;
481 }
482 #endif
483 
SockList_Init
void SockList_Init(SockList *sl)
Definition: lowlevel.c:52
global.h
SockList_AddInt64
void SockList_AddInt64(SockList *sl, uint64_t data)
Definition: lowlevel.c:137
CS_Stats::ibytes
int ibytes
Definition: newclient.h:695
llevError
@ llevError
Definition: logger.h:11
socket_struct
Definition: newserver.h:89
Socket_Info::allocated_sockets
int allocated_sockets
Definition: newserver.h:144
SockList_AddChar
void SockList_AddChar(SockList *sl, unsigned char data)
Definition: lowlevel.c:103
SockList_AddInt
void SockList_AddInt(SockList *sl, uint32_t data)
Definition: lowlevel.c:124
socket_info
Socket_Info socket_info
Definition: init.c:49
Write_To_Socket
static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, const int len)
Definition: lowlevel.c:396
GetInt_String
int GetInt_String(const unsigned char *data)
Definition: lowlevel.c:251
SockList_AddPrintf
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.c:199
SockList_AddLen8Data
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.c:176
SockList_NullTerminate
void SockList_NullTerminate(SockList *sl)
Definition: lowlevel.c:234
SockList_AddLen16Data
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.c:188
Ns_Dead
@ Ns_Dead
Definition: newserver.h:67
SockList_Ensure
static void SockList_Ensure(const SockList *sl, size_t size)
Definition: lowlevel.c:92
CS_Stats::time_start
time_t time_start
Definition: newclient.h:698
Send_With_Handling
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.c:440
fatal
void fatal(enum fatal_error err)
Definition: utils.c:580
count_all_players
int count_all_players(void)
Definition: metaserver.c:72
SockList_AddData
void SockList_AddData(SockList *sl, const void *data, size_t len)
Definition: lowlevel.c:164
navar-midane_time.data
data
Definition: navar-midane_time.py:11
stringbuffer_finish
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.c:76
sproto.h
CS_Stats::obytes
int obytes
Definition: newclient.h:696
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
SockList::len
size_t len
Definition: newclient.h:686
SockList_AddShort
void SockList_AddShort(SockList *sl, uint16_t data)
Definition: lowlevel.c:113
StringBuffer
Definition: stringbuffer.c:25
cst_tot
CS_Stats cst_tot
GetShort_String
short GetShort_String(const unsigned char *data)
Definition: lowlevel.c:255
llevInfo
@ llevInfo
Definition: logger.h:12
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
write_cs_stats
void write_cs_stats(void)
socket_struct::status
enum Sock_Status status
Definition: newserver.h:90
SockList_Reset
void SockList_Reset(SockList *sl)
Definition: lowlevel.c:71
send
Definition: send.py:1
SockList_ResetRead
void SockList_ResetRead(SockList *sl)
Definition: lowlevel.c:80
buf
StringBuffer * buf
Definition: readable.c:1610
SockList_Avail
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.c:243
newclient.h
SockList_Term
void SockList_Term(SockList *sl)
Definition: lowlevel.c:62
SockList_AddString
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.c:154
socket_struct::fd
int fd
Definition: newserver.h:91
SockList_AddStringBuffer
void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb)
Definition: lowlevel.c:222
count_players
int count_players(void)
Definition: metaserver.c:46
cst_lst
CS_Stats cst_lst
Definition: newclient.h:701
SockList::buf
unsigned char buf[MAXSOCKBUF]
Definition: newclient.h:687
CS_Stats
Definition: newclient.h:694
OUT_OF_MEMORY
@ OUT_OF_MEMORY
Definition: define.h:48
SockList_ReadPacket
int SockList_ReadPacket(int fd, SockList *sl, int len)
Definition: lowlevel.c:272
if
if(!(yy_init))
Definition: loader.c:2626
SockList
Definition: newclient.h:681
llevDebug
@ llevDebug
Definition: logger.h:13
CS_Stats::max_conn
short max_conn
Definition: newclient.h:697