Crossfire Server, Trunk  R20513
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 /***********************************************************************
38  *
39  * SockList functions/utilities
40  *
41  **********************************************************************/
42 
49  SockList_Reset(sl);
50 }
51 
59 }
60 
67  sl->len = 2;
68 }
69 
76  sl->len = 0;
77 }
78 
87 static void SockList_Ensure(const SockList *sl, size_t size) {
88  if (sl->len+size > sizeof(sl->buf)) {
90  }
91 }
92 
98 void SockList_AddChar(SockList *sl, char data) {
99  SockList_Ensure(sl, 1);
100  sl->buf[sl->len++] = data;
101 }
102 
109  SockList_Ensure(sl, 2);
110  sl->buf[sl->len++] = (data>>8)&0xff;
111  sl->buf[sl->len++] = data&0xff;
112 }
113 
120  SockList_Ensure(sl, 4);
121  sl->buf[sl->len++] = (data>>24)&0xff;
122  sl->buf[sl->len++] = (data>>16)&0xff;
123  sl->buf[sl->len++] = (data>>8)&0xff;
124  sl->buf[sl->len++] = data&0xff;
125 }
126 
133  SockList_Ensure(sl, 8);
134  sl->buf[sl->len++] = (char)((data>>56)&0xff);
135  sl->buf[sl->len++] = (char)((data>>48)&0xff);
136  sl->buf[sl->len++] = (char)((data>>40)&0xff);
137  sl->buf[sl->len++] = (char)((data>>32)&0xff);
138  sl->buf[sl->len++] = (char)((data>>24)&0xff);
139  sl->buf[sl->len++] = (char)((data>>16)&0xff);
140  sl->buf[sl->len++] = (char)((data>>8)&0xff);
141  sl->buf[sl->len++] = (char)(data&0xff);
142 }
143 
149 void SockList_AddString(SockList *sl, const char *data) {
150  SockList_AddData(sl, data, strlen(data));
151 }
152 
159 void SockList_AddData(SockList *sl, const void *data, size_t len) {
160  SockList_Ensure(sl, len);
161  memcpy(sl->buf+sl->len, data, len);
162  sl->len += len;
163 }
164 
171 void SockList_AddLen8Data(SockList *sl, const void *data, size_t len) {
172  assert(len <= 255);
173  SockList_AddChar(sl, len);
174  SockList_AddData(sl, data, len);
175 }
176 
183 void SockList_AddLen16Data(SockList *sl, const void *data, size_t len) {
184  assert(len <= 65535);
185  SockList_AddShort(sl, len);
186  SockList_AddData(sl, data, len);
187 }
188 
194 void SockList_AddPrintf(SockList *sl, const char *format, ...) {
195  size_t size;
196  int n;
197  va_list arg;
198 
199  size = sizeof(sl->buf)-sl->len;
200 
201  va_start(arg, format);
202  n = vsnprintf((char *)sl->buf+sl->len, size, format, arg);
203  va_end(arg);
204 
205  if (n <= -1 || (size_t)n >= size) {
207  }
208  sl->len += (size_t)n;
209 }
210 
218  char *p;
219 
220  p = stringbuffer_finish(sb);
221  SockList_AddString(sl, p);
222  free(p);
223 }
224 
230  SockList_Ensure(sl, 1);
231  sl->buf[sl->len] = '\0';
232 }
233 
238 size_t SockList_Avail(const SockList *sl) {
239  return sizeof(sl->buf)-sl->len;
240 }
241 
246 int GetInt_String(const unsigned char *data) {
247  return ((data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]);
248 }
249 
250 short GetShort_String(const unsigned char *data) {
251  return ((data[0]<<8)+data[1]);
252 }
253 
254 /******************************************************************************
255  *
256  * Start of read routines.
257  *
258  ******************************************************************************/
259 
267 int SockList_ReadPacket(int fd, SockList *sl, int len) {
268  int stat, toread;
269 
270  /* We already have a partial packet */
271  if (sl->len < 2) {
272 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: change read() to recv() */
273 
274  stat = recv(fd, sl->buf+sl->len, 2-sl->len, 0);
275 
276 #else
277  do {
278  stat = read(fd, sl->buf+sl->len, 2-sl->len);
279  } while ((stat == -1) && (errno == EINTR));
280 #endif
281  if (stat < 0) {
282  /* In non blocking mode, EAGAIN is set when there is no
283  * data available.
284  */
285 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: error handling for win32 */
286  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
287  if (WSAGetLastError() == WSAECONNRESET)
288  LOG(llevDebug, "Connection closed by client\n");
289  else {
290  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
291  }
292  return -1; /* kick this user! */
293  }
294 #else
295  if (errno == ECONNRESET) {
296  LOG(llevDebug, "ReadPacket got error %s, returning -1\n", strerror(errno));
297  return -1;
298  }
299  if (errno != EAGAIN && errno != EWOULDBLOCK) {
300  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
301  }
302 #endif
303  return 0; /*Error */
304  }
305  if (stat == 0)
306  return -1;
307  sl->len += stat;
308 #ifdef CS_LOGSTATS
309  cst_tot.ibytes += stat;
310  cst_lst.ibytes += stat;
311 #endif
312  if (stat < 2)
313  return 0; /* Still don't have a full packet */
314  }
315  /* Figure out how much more data we need to read. Add 2 from the
316  * end of this - size header information is not included.
317  */
318  toread = 2+(sl->buf[0]<<8)+sl->buf[1]-sl->len;
319  if ((toread+(int)sl->len) >= len) {
320  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);
321  /* Quick hack in case for 'oldsocketmode' input. If we are
322  * closing the socket anyways, then reading this extra 100 bytes
323  * shouldn't hurt.
324  */
325 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
326  recv(fd, sl->buf+2, 100, 0);
327 #else
328  read(fd, sl->buf+2, 100);
329 #endif /* end win32 */
330 
331  /* return error so the socket is closed */
332  return -1;
333  }
334  do {
335 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
336  stat = recv(fd, sl->buf+sl->len, toread, 0);
337 #else
338  do {
339  stat = read(fd, sl->buf+sl->len, toread);
340  } while ((stat < 0) && (errno == EINTR));
341 #endif
342  if (stat < 0) {
343 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change error handling for win32 */
344  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
345  if (WSAGetLastError() == WSAECONNRESET)
346  LOG(llevDebug, "Connection closed by client\n");
347  else {
348  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
349  }
350  return -1; /* kick this user! */
351  }
352 #else
353  if (errno != EAGAIN && errno != EWOULDBLOCK) {
354  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror(errno));
355  }
356 #endif
357  return 0; /*Error */
358  }
359  if (stat == 0)
360  return -1;
361  sl->len += stat;
362 #ifdef CS_LOGSTATS
363  cst_tot.ibytes += stat;
364  cst_lst.ibytes += stat;
365 #endif
366  toread -= stat;
367  if (toread == 0)
368  return 1;
369  if (toread < 0) {
370  LOG(llevError, "SockList_ReadPacket: Read more bytes than desired.\n");
371  return 1;
372  }
373  } while (toread > 0);
374  return 0;
375 }
376 
377 /*******************************************************************************
378  *
379  * Start of write related routines.
380  *
381  ******************************************************************************/
382 
389 static void add_to_buffer(socket_struct *ns, const unsigned char *buf, int len) {
390  int avail, end;
391 
392  if ((len+ns->outputbuffer.len) > SOCKETBUFSIZE) {
393  LOG(llevDebug, "Socket on fd %d has overrun internal buffer - marking as dead\n", ns->fd);
394  ns->status = Ns_Dead;
395  return;
396  }
397 
398  /* data + end is where we start putting the new data. The last byte
399  * currently in use is actually data + end -1
400  */
401 
402  end = ns->outputbuffer.start+ns->outputbuffer.len;
403  /* The buffer is already in a wrapped state, so adjust end */
404  if (end >= SOCKETBUFSIZE)
405  end -= SOCKETBUFSIZE;
406  avail = SOCKETBUFSIZE-end;
407 
408  /* We can all fit it behind the current data without wrapping */
409  if (avail >= len) {
410  memcpy(ns->outputbuffer.data+end, buf, len);
411  } else {
412  memcpy(ns->outputbuffer.data+end, buf, avail);
413  memcpy(ns->outputbuffer.data, buf+avail, len-avail);
414  }
415  ns->outputbuffer.len += len;
416 }
417 
425  int amt, max;
426 
427  if (ns->outputbuffer.len == 0) {
428  LOG(llevDebug, "write_socket_buffer called when there is no data, fd=%d\n", ns->fd);
429  return;
430  }
431 
432  do {
433  max = SOCKETBUFSIZE-ns->outputbuffer.start;
434  if (ns->outputbuffer.len < max)
435  max = ns->outputbuffer.len;
436 
437 #ifdef WIN32 /* ***win32 write_socket_buffer: change write() to send() */
438  amt = send(ns->fd, ns->outputbuffer.data+ns->outputbuffer.start, max, 0);
439 #else
440  do {
441  amt = write(ns->fd, ns->outputbuffer.data+ns->outputbuffer.start, max);
442  } while ((amt < 0) && (errno == EINTR));
443 #endif
444 
445  if (amt < 0) { /* We got an error */
446 #ifdef WIN32 /* ***win32 write_socket_buffer: change error handling */
447  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
448  LOG(llevError, "New socket write failed (wsb) (%d).\n", WSAGetLastError());
449 #else
450  if (errno != EWOULDBLOCK) {
451  LOG(llevError, "New socket write failed (wsb): %s\n", strerror(errno));
452 #endif
453  ns->status = Ns_Dead;
454  return;
455  } else { /* EWOULDBLOCK */
456  /* can't write it, so store it away. */
457  ns->can_write = 0;
458  return;
459  }
460  }
461  ns->outputbuffer.start += amt;
462  /* wrap back to start of buffer */
463  if (ns->outputbuffer.start == SOCKETBUFSIZE)
464  ns->outputbuffer.start = 0;
465  ns->outputbuffer.len -= amt;
466 #ifdef CS_LOGSTATS
467  cst_tot.obytes += amt;
468  cst_lst.obytes += amt;
469 #endif
470  } while (ns->outputbuffer.len > 0);
471 }
472 
480 static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, int len) {
481  int amt = 0;
482  const unsigned char *pos = buf;
483 
484  if (ns->status == Ns_Dead || !buf) {
485  LOG(llevDebug, "Write_To_Socket called with dead socket\n");
486  return;
487  }
488 
489 #ifndef __GNU__ /* This caused problems on Hurd */
490  if (!ns->can_write) {
491  add_to_buffer(ns, buf, len);
492  return;
493  }
494 #endif
495  /* If we manage to write more than we wanted, take it as a bonus */
496  while (len > 0) {
497 #ifdef WIN32 /* ***win32 Write_To_Socket: change write() to send() */
498  amt = send(ns->fd, pos, len, 0);
499 #else
500  do {
501  amt = write(ns->fd, pos, len);
502  } while ((amt < 0) && (errno == EINTR));
503 #endif
504 
505  if (amt < 0) { /* We got an error */
506 #ifdef WIN32 /* ***win32 Write_To_Socket: change error handling */
507  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
508  LOG(llevError, "New socket write failed WTS (%d).\n", WSAGetLastError());
509 #else
510  if (errno != EWOULDBLOCK) {
511  LOG(llevError, "New socket write failed WTS: %s\n", strerror(errno));
512 #endif
513  ns->status = Ns_Dead;
514  return;
515  } else { /* EWOULDBLOCK */
516  /* can't write it, so store it away. */
517  add_to_buffer(ns, pos, len);
518  ns->can_write = 0;
519  return;
520  }
521  /* amt gets set to 0 above in blocking code, so we do this as
522  * an else if to make sure we don't reprocess it.
523  */
524  } else if (amt == 0) {
525  LOG(llevError, "Write_To_Socket: No data written out.\n");
526  }
527  len -= amt;
528  pos += amt;
529 #ifdef CS_LOGSTATS
530  cst_tot.obytes += amt;
531  cst_lst.obytes += amt;
532 #endif
533  }
534 }
535 
543  if (ns->status == Ns_Dead || sl == NULL)
544  return;
545 
546  sl->buf[0] = ((sl->len-2)>>8)&0xFF;
547  sl->buf[1] = (sl->len-2)&0xFF;
548  Write_To_Socket(ns, sl->buf, sl->len);
549 }
550 
551 /******************************************************************************
552  *
553  * statistics logging functions.
554  *
555  ******************************************************************************/
556 
557 #ifdef CS_LOGSTATS
558 /* cst_tot is for the life of the server, cst_last is for the last series of
559  * stats
560  */
562 
566 void write_cs_stats(void) {
567  time_t now = time(NULL);
568 
569  /* If no connections recently, don't bother to log anything */
570  if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
571  return;
572 
573  /* CSSTAT is put in so scripts can easily find the line */
574  LOG(llevInfo, "CSSTAT: %.16s tot %d %d %d %ld inc %d %d %d %ld\n",
575  ctime(&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
576  (long)(now-cst_tot.time_start), cst_lst.ibytes, cst_lst.obytes,
577  cst_lst.max_conn, (long)(now-cst_lst.time_start));
578  cst_lst.ibytes = 0;
579  cst_lst.obytes = 0;
581  cst_lst.time_start = now;
582 }
583 #endif
584 
Error, serious thing.
Definition: logger.h:11
void SockList_AddData(SockList *sl, const void *data, size_t len)
Adds a data block.
Definition: lowlevel.c:159
void SockList_AddChar(SockList *sl, char data)
Adds an 8 bit value.
Definition: lowlevel.c:98
size_t len
Definition: newclient.h:685
Information.
Definition: logger.h:12
unsigned char buf[MAXSOCKBUF]
Definition: newclient.h:686
int obytes
Definition: newclient.h:695
Defines various flags that both the new client and new server use.
void write_socket_buffer(socket_struct *ns)
Writes data to socket.
Definition: lowlevel.c:424
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.c:596
short GetShort_String(const unsigned char *data)
Definition: lowlevel.c:250
size_t SockList_Avail(const SockList *sl)
Returns the available bytes in a SockList instance.
Definition: lowlevel.c:238
void SockList_Init(SockList *sl)
Initializes the SockList instance.
Definition: lowlevel.c:48
Socket structure, represents a client-server connection.
Definition: newserver.h:99
static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, int len)
This writes data to the socket.
Definition: lowlevel.c:480
void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb)
Deallocates string buffer instance and appends its contents.
Definition: lowlevel.c:217
enum Sock_Status status
Definition: newserver.h:100
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 8 bit length field.
Definition: lowlevel.c:171
Global type definitions and header inclusions.
buffer_struct outputbuffer
For undeliverable data.
Definition: newserver.h:111
Socket_Info socket_info
Socket information.
Definition: init.c:47
CS_Stats cst_lst
short max_conn
Maximum connections received.
Definition: newclient.h:696
int ibytes
ibytes, obytes are bytes in, out.
Definition: newclient.h:694
#define SOCKETBUFSIZE
SOCKETBUFSIZE is the size of the buffer used internally by the server for storing backlogged messages...
Definition: config.h:521
static void SockList_Ensure(const SockList *sl, size_t size)
Checks that at least a given number of bytes is available in a SockList instance. ...
Definition: lowlevel.c:87
void SockList_NullTerminate(SockList *sl)
Adds a NUL byte without changing the length.
Definition: lowlevel.c:229
int GetInt_String(const unsigned char *data)
Basically does the reverse of SockList_AddInt, but on strings instead.
Definition: lowlevel.c:246
uint32_t can_write
Can we write to this socket?
Definition: newserver.h:117
void SockList_AddPrintf(SockList *sl, const char *format,...)
Adds a printf like formatted string.
Definition: lowlevel.c:194
unsigned __int64 uint64_t
Definition: win32.h:167
int allocated_sockets
Number of allocated items in init_sockets.
Definition: newserver.h:156
void SockList_AddString(SockList *sl, const char *data)
Adds a string without length.
Definition: lowlevel.c:149
static void add_to_buffer(socket_struct *ns, const unsigned char *buf, int len)
Adds data to a socket buffer for whatever reason.
Definition: lowlevel.c:389
void SockList_AddInt(SockList *sl, uint32_t data)
Adds a 32 bit value.
Definition: lowlevel.c:119
void Send_With_Handling(socket_struct *ns, SockList *sl)
Calls Write_To_Socket to send data to the client.
Definition: lowlevel.c:542
unsigned short uint16_t
Definition: win32.h:163
unsigned int uint32_t
Definition: win32.h:162
#define vsnprintf
Definition: win32.h:61
void write_cs_stats(void)
Only for debugging purposes.
Definition: logger.h:13
CS_Stats cst_tot
Statistics on server.
Definition: newclient.h:693
void SockList_Term(SockList *sl)
Frees all resources allocated by a SockList instance.
Definition: lowlevel.c:58
void SockList_AddShort(SockList *sl, uint16_t data)
Adds a 16 bit value.
Definition: lowlevel.c:108
void SockList_AddLen16Data(SockList *sl, const void *data, size_t len)
Adds a data block prepended with an 16 bit length field.
Definition: lowlevel.c:183
time_t time_start
When we started logging this.
Definition: newclient.h:697
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
int SockList_ReadPacket(int fd, SockList *sl, int len)
This reads from fd and puts the data in sl.
Definition: lowlevel.c:267
void SockList_Reset(SockList *sl)
Resets the length of the stored data for writing.
Definition: lowlevel.c:66
char data[SOCKETBUFSIZE]
Definition: newserver.h:88
A buffer that will be expanded as content is added to it.
Definition: stringbuffer.c:25
Contains the base information we use to make up a packet we want to send.
Definition: newclient.h:680
void SockList_ResetRead(SockList *sl)
Resets the length of the stored data for reading.
Definition: lowlevel.c:75
void SockList_AddInt64(SockList *sl, uint64_t data)
Adds a 64 bit value.
Definition: lowlevel.c:132
char * stringbuffer_finish(StringBuffer *sb)
Deallocate the string buffer instance and return the string.
Definition: stringbuffer.c:76