Crossfire Server, Branches 1.12  R18729
lowlevel.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_sockets_c =
3  * "$Id: lowlevel.c 11578 2009-02-23 22:02:27Z lalo $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 1992 Frank Tore Johansen
10 
11  This program is free software; you can redistribute it and/or modify
12  it under the terms of the GNU General Public License as published by
13  the Free Software Foundation; either version 2 of the License, or
14  (at your option) any later version.
15 
16  This program is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  GNU General Public License for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with this program; if not, write to the Free Software
23  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24 
25  The author can be reached via e-mail to mark@pyramid.com
26 */
27 
40 #include <assert.h>
41 #include <stdarg.h>
42 #include <global.h>
43 #include <newclient.h>
44 #include <sproto.h>
45 #include <errno.h>
46 
54 static void SockList_Ensure(const SockList *sl, size_t size);
55 
56 /***********************************************************************
57  *
58  * SockList functions/utilities
59  *
60  **********************************************************************/
61 
68  SockList_Reset(sl);
69 }
70 
78 }
79 
86  sl->len = 2;
87 }
88 
95  sl->len = 0;
96 }
97 
103 void SockList_AddChar(SockList *sl, char data) {
104  SockList_Ensure(sl, 1);
105  sl->buf[sl->len++] = data;
106 }
107 
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 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 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 
187 void SockList_AddPrintf(SockList *sl, const char *format, ...) {
188  size_t size;
189  int n;
190  va_list arg;
191 
192  size = sizeof(sl->buf)-sl->len;
193 
194  va_start(arg, format);
195  n = vsnprintf((char *)sl->buf+sl->len, size, format, arg);
196  va_end(arg);
197 
198  if (n <= -1 || (size_t)n >= size) {
200  }
201  sl->len += (size_t)n;
202 }
203 
211  char *p;
212 
213  p = stringbuffer_finish(sb);
214  SockList_AddString(sl, p);
215  free(p);
216 }
217 
223  SockList_Ensure(sl, 1);
224  sl->buf[sl->len] = '\0';
225 }
226 
231 size_t SockList_Avail(const SockList *sl) {
232  return sizeof(sl->buf)-sl->len;
233 }
234 
239 int GetInt_String(const unsigned char *data) {
240  return ((data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]);
241 }
242 
243 short GetShort_String(const unsigned char *data) {
244  return ((data[0]<<8)+data[1]);
245 }
246 
247 /******************************************************************************
248  *
249  * Start of read routines.
250  *
251  ******************************************************************************/
252 
260 int SockList_ReadPacket(int fd, SockList *sl, int len) {
261  int stat, toread;
262  char err[MAX_BUF];
263 
264  /* We already have a partial packet */
265  if (sl->len < 2) {
266 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: change read() to recv() */
267 
268  stat = recv(fd, sl->buf+sl->len, 2-sl->len, 0);
269 
270 #else
271  do {
272  stat = read(fd, sl->buf+sl->len, 2-sl->len);
273  } while ((stat == -1) && (errno == EINTR));
274 #endif
275  if (stat < 0) {
276  /* In non blocking mode, EAGAIN is set when there is no
277  * data available.
278  */
279 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: error handling for win32 */
280  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
281  if (WSAGetLastError() == WSAECONNRESET)
282  LOG(llevDebug, "Connection closed by client\n");
283  else {
284  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
285  }
286  return -1; /* kick this user! */
287  }
288 #else
289  if (errno == ECONNRESET) {
290  LOG(llevDebug, "ReadPacket got error %s, returning -1\n", strerror_local(errno, err, sizeof(err)));
291  return -1;
292  }
293  if (errno != EAGAIN && errno != EWOULDBLOCK) {
294  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror_local(errno, err, sizeof(err)));
295  }
296 #endif
297  return 0; /*Error */
298  }
299  if (stat == 0)
300  return -1;
301  sl->len += stat;
302 #ifdef CS_LOGSTATS
303  cst_tot.ibytes += stat;
304  cst_lst.ibytes += stat;
305 #endif
306  if (stat < 2)
307  return 0; /* Still don't have a full packet */
308  }
309  /* Figure out how much more data we need to read. Add 2 from the
310  * end of this - size header information is not included.
311  */
312  toread = 2+(sl->buf[0]<<8)+sl->buf[1]-sl->len;
313  if ((toread+(int)sl->len) >= len) {
314  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);
315  /* Quick hack in case for 'oldsocketmode' input. If we are
316  * closing the socket anyways, then reading this extra 100 bytes
317  * shouldn't hurt.
318  */
319 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
320  recv(fd, sl->buf+2, 100, 0);
321 #else
322  read(fd, sl->buf+2, 100);
323 #endif /* end win32 */
324 
325  /* return error so the socket is closed */
326  return -1;
327  }
328  do {
329 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */
330  stat = recv(fd, sl->buf+sl->len, toread, 0);
331 #else
332  do {
333  stat = read(fd, sl->buf+sl->len, toread);
334  } while ((stat < 0) && (errno == EINTR));
335 #endif
336  if (stat < 0) {
337 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change error handling for win32 */
338  if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) {
339  if (WSAGetLastError() == WSAECONNRESET)
340  LOG(llevDebug, "Connection closed by client\n");
341  else {
342  LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError());
343  }
344  return -1; /* kick this user! */
345  }
346 #else
347  if (errno != EAGAIN && errno != EWOULDBLOCK) {
348  LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror_local(errno, err, sizeof(err)));
349  }
350 #endif
351  return 0; /*Error */
352  }
353  if (stat == 0)
354  return -1;
355  sl->len += stat;
356 #ifdef CS_LOGSTATS
357  cst_tot.ibytes += stat;
358  cst_lst.ibytes += stat;
359 #endif
360  toread -= stat;
361  if (toread == 0)
362  return 1;
363  if (toread < 0) {
364  LOG(llevError, "SockList_ReadPacket: Read more bytes than desired.\n");
365  return 1;
366  }
367  } while (toread > 0);
368  return 0;
369 }
370 
371 /*******************************************************************************
372  *
373  * Start of write related routines.
374  *
375  ******************************************************************************/
376 
383 static void add_to_buffer(socket_struct *ns, const unsigned char *buf, int len) {
384  int avail, end;
385 
386  if ((len+ns->outputbuffer.len) > SOCKETBUFSIZE) {
387  LOG(llevDebug, "Socket on fd %d has overrun internal buffer - marking as dead\n", ns->fd);
388  ns->status = Ns_Dead;
389  return;
390  }
391 
392  /* data + end is where we start putting the new data. The last byte
393  * currently in use is actually data + end -1
394  */
395 
396  end = ns->outputbuffer.start+ns->outputbuffer.len;
397  /* The buffer is already in a wrapped state, so adjust end */
398  if (end >= SOCKETBUFSIZE)
399  end -= SOCKETBUFSIZE;
400  avail = SOCKETBUFSIZE-end;
401 
402  /* We can all fit it behind the current data without wrapping */
403  if (avail >= len) {
404  memcpy(ns->outputbuffer.data+end, buf, len);
405  } else {
406  memcpy(ns->outputbuffer.data+end, buf, avail);
407  memcpy(ns->outputbuffer.data, buf+avail, len-avail);
408  }
409  ns->outputbuffer.len += len;
410 }
411 
419  int amt, max;
420 
421  if (ns->outputbuffer.len == 0) {
422  LOG(llevDebug, "write_socket_buffer called when there is no data, fd=%d\n", ns->fd);
423  return;
424  }
425 
426  do {
427  max = SOCKETBUFSIZE-ns->outputbuffer.start;
428  if (ns->outputbuffer.len < max)
429  max = ns->outputbuffer.len;
430 
431 #ifdef WIN32 /* ***win32 write_socket_buffer: change write() to send() */
432  amt = send(ns->fd, ns->outputbuffer.data+ns->outputbuffer.start, max, 0);
433 #else
434  do {
435  amt = write(ns->fd, ns->outputbuffer.data+ns->outputbuffer.start, max);
436  } while ((amt < 0) && (errno == EINTR));
437 #endif
438 
439  if (amt < 0) { /* We got an error */
440 
441 #ifdef WIN32 /* ***win32 write_socket_buffer: change error handling */
442  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
443  LOG(llevError, "New socket write failed (wsb) (%d).\n", WSAGetLastError());
444 #else
445  if (errno != EWOULDBLOCK) {
446  char err[MAX_BUF];
447 
448  LOG(llevError, "New socket write failed (wsb) (%d: %s).\n", errno, strerror_local(errno, err, sizeof(err)));
449 #endif
450  ns->status = Ns_Dead;
451  return;
452  } else { /* EWOULDBLOCK */
453  /* can't write it, so store it away. */
454  ns->can_write = 0;
455  return;
456  }
457  }
458  ns->outputbuffer.start += amt;
459  /* wrap back to start of buffer */
460  if (ns->outputbuffer.start == SOCKETBUFSIZE)
461  ns->outputbuffer.start = 0;
462  ns->outputbuffer.len -= amt;
463 #ifdef CS_LOGSTATS
464  cst_tot.obytes += amt;
465  cst_lst.obytes += amt;
466 #endif
467  } while (ns->outputbuffer.len > 0);
468 }
469 
477 static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, int len) {
478  int amt = 0;
479  const unsigned char *pos = buf;
480  char err[MAX_BUF];
481 
482  if (ns->status == Ns_Dead || !buf) {
483  LOG(llevDebug, "Write_To_Socket called with dead socket\n");
484  return;
485  }
486 
487 #ifndef __GNU__ /* This caused problems on Hurd */
488  if (!ns->can_write) {
489  add_to_buffer(ns, buf, len);
490  return;
491  }
492 #endif
493  /* If we manage to write more than we wanted, take it as a bonus */
494  while (len > 0) {
495 
496 #ifdef WIN32 /* ***win32 Write_To_Socket: change write() to send() */
497  amt = send(ns->fd, pos, len, 0);
498 #else
499  do {
500  amt = write(ns->fd, pos, len);
501  } while ((amt < 0) && (errno == EINTR));
502 #endif
503 
504  if (amt < 0) { /* We got an error */
505 #ifdef WIN32 /* ***win32 Write_To_Socket: change error handling */
506  if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) {
507  LOG(llevError, "New socket write failed WTS (%d).\n", WSAGetLastError());
508 #else
509  if (errno != EWOULDBLOCK) {
510  LOG(llevError, "New socket write failed WTS (%d: %s).\n", errno, strerror_local(errno, err, sizeof(err)));
511 #endif
512  ns->status = Ns_Dead;
513  return;
514  } else { /* EWOULDBLOCK */
515  /* can't write it, so store it away. */
516  add_to_buffer(ns, pos, len);
517  ns->can_write = 0;
518  return;
519  }
520  /* amt gets set to 0 above in blocking code, so we do this as
521  * an else if to make sure we don't reprocess it.
522  */
523  } else if (amt == 0) {
524  LOG(llevError, "Write_To_Socket: No data written out.\n");
525  }
526  len -= amt;
527  pos += amt;
528 #ifdef CS_LOGSTATS
529  cst_tot.obytes += amt;
530  cst_lst.obytes += amt;
531 #endif
532  }
533 }
534 
542  if (ns->status == Ns_Dead || sl == NULL)
543  return;
544 
545  sl->buf[0] = ((sl->len-2)>>8)&0xFF;
546  sl->buf[1] = (sl->len-2)&0xFF;
547  Write_To_Socket(ns, sl->buf, sl->len);
548 }
549 
550 /******************************************************************************
551  *
552  * statistics logging functions.
553  *
554  ******************************************************************************/
555 
556 #ifdef CS_LOGSTATS
557 /* cst_tot is for the life of the server, cst_last is for the last series of
558  * stats
559  */
561 
565 void write_cs_stats(void) {
566  time_t now = time(NULL);
567 
568  /* If no connections recently, don't both to log anything */
569  if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
570  return;
571 
572  /* CSSTAT is put in so scripts can easily find the line */
573  LOG(llevInfo, "CSSTAT: %.16s tot %d %d %d %ld inc %d %d %d %ld\n",
574  ctime(&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn,
575  (long)(now-cst_tot.time_start), cst_lst.ibytes, cst_lst.obytes,
576  cst_lst.max_conn, (long)(now-cst_lst.time_start));
577  cst_lst.ibytes = 0;
578  cst_lst.obytes = 0;
579  cst_lst.max_conn = socket_info.nconns;
580  cst_lst.time_start = now;
581 }
582 #endif
583 
584 static void SockList_Ensure(const SockList *sl, size_t size) {
585  if (sl->len+size > sizeof(sl->buf)) {
587  }
588 }
void SockList_AddData(SockList *sl, const void *data, size_t len)
Definition: lowlevel.c:164
void SockList_AddChar(SockList *sl, char data)
Definition: lowlevel.c:103
size_t len
Definition: newclient.h:575
#define OUT_OF_MEMORY
Definition: define.h:94
uint32 can_write
Definition: newserver.h:132
int obytes
Definition: newclient.h:582
void write_socket_buffer(socket_struct *ns)
Definition: lowlevel.c:418
short GetShort_String(const unsigned char *data)
Definition: lowlevel.c:243
size_t SockList_Avail(const SockList *sl)
Definition: lowlevel.c:231
void SockList_Init(SockList *sl)
Definition: lowlevel.c:67
static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, int len)
Definition: lowlevel.c:477
void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb)
Definition: lowlevel.c:210
enum Sock_Status status
Definition: newserver.h:116
void SockList_AddLen8Data(SockList *sl, const void *data, size_t len)
Definition: lowlevel.c:176
void SockList_AddInt(SockList *sl, uint32 data)
Definition: lowlevel.c:124
unsigned char buf[2+65536UL+1]
Definition: newclient.h:576
buffer_struct outputbuffer
Definition: newserver.h:126
void SockList_AddInt64(SockList *sl, uint64 data)
Definition: lowlevel.c:137
Socket_Info socket_info
Definition: init.c:63
CS_Stats cst_lst
short max_conn
Definition: newclient.h:583
int ibytes
Definition: newclient.h:581
#define SOCKETBUFSIZE
Definition: config.h:525
static void SockList_Ensure(const SockList *sl, size_t size)
Definition: lowlevel.c:584
void SockList_NullTerminate(SockList *sl)
Definition: lowlevel.c:222
int GetInt_String(const unsigned char *data)
Definition: lowlevel.c:239
void fatal(int err)
Definition: glue.c:60
void SockList_AddPrintf(SockList *sl, const char *format,...)
Definition: lowlevel.c:187
void SockList_AddString(SockList *sl, const char *data)
Definition: lowlevel.c:154
#define MAX_BUF
Definition: define.h:81
static void add_to_buffer(socket_struct *ns, const unsigned char *buf, int len)
Definition: lowlevel.c:383
void Send_With_Handling(socket_struct *ns, SockList *sl)
Definition: lowlevel.c:541
unsigned short uint16
Definition: global.h:67
#define vsnprintf
Definition: win32.h:72
void write_cs_stats(void)
CS_Stats cst_tot
void SockList_Term(SockList *sl)
Definition: lowlevel.c:77
time_t time_start
Definition: newclient.h:584
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
unsigned int uint32
Definition: global.h:58
int SockList_ReadPacket(int fd, SockList *sl, int len)
Definition: lowlevel.c:260
void SockList_Reset(SockList *sl)
Definition: lowlevel.c:85
char data[SOCKETBUFSIZE]
Definition: newserver.h:104
char * strerror_local(int errnum, char *buf, size_t size)
Definition: porting.c:525
void SockList_ResetRead(SockList *sl)
Definition: lowlevel.c:94
void SockList_AddShort(SockList *sl, uint16 data)
Definition: lowlevel.c:113
char * stringbuffer_finish(StringBuffer *sb)
Definition: stringbuffer.c:78