Crossfire Server, Branch 1.12
R12190
|
00001 /* 00002 * static char *rcsid_sockets_c = 00003 * "$Id: lowlevel.c 11578 2009-02-23 22:02:27Z lalo $"; 00004 */ 00005 00006 /* 00007 CrossFire, A Multiplayer game for X-windows 00008 00009 Copyright (C) 1992 Frank Tore Johansen 00010 00011 This program is free software; you can redistribute it and/or modify 00012 it under the terms of the GNU General Public License as published by 00013 the Free Software Foundation; either version 2 of the License, or 00014 (at your option) any later version. 00015 00016 This program is distributed in the hope that it will be useful, 00017 but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 GNU General Public License for more details. 00020 00021 You should have received a copy of the GNU General Public License 00022 along with this program; if not, write to the Free Software 00023 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00024 00025 The author can be reached via e-mail to mark@pyramid.com 00026 */ 00027 00040 #include <assert.h> 00041 #include <stdarg.h> 00042 #include <global.h> 00043 #include <newclient.h> 00044 #include <sproto.h> 00045 #include <errno.h> 00046 00054 static void SockList_Ensure(const SockList *sl, size_t size); 00055 00056 /*********************************************************************** 00057 * 00058 * SockList functions/utilities 00059 * 00060 **********************************************************************/ 00061 00067 void SockList_Init(SockList *sl) { 00068 SockList_Reset(sl); 00069 } 00070 00077 void SockList_Term(SockList *sl) { 00078 } 00079 00085 void SockList_Reset(SockList *sl) { 00086 sl->len = 2; 00087 } 00088 00094 void SockList_ResetRead(SockList *sl) { 00095 sl->len = 0; 00096 } 00097 00103 void SockList_AddChar(SockList *sl, char data) { 00104 SockList_Ensure(sl, 1); 00105 sl->buf[sl->len++] = data; 00106 } 00107 00113 void SockList_AddShort(SockList *sl, uint16 data) { 00114 SockList_Ensure(sl, 2); 00115 sl->buf[sl->len++] = (data>>8)&0xff; 00116 sl->buf[sl->len++] = data&0xff; 00117 } 00118 00124 void SockList_AddInt(SockList *sl, uint32 data) { 00125 SockList_Ensure(sl, 4); 00126 sl->buf[sl->len++] = (data>>24)&0xff; 00127 sl->buf[sl->len++] = (data>>16)&0xff; 00128 sl->buf[sl->len++] = (data>>8)&0xff; 00129 sl->buf[sl->len++] = data&0xff; 00130 } 00131 00137 void SockList_AddInt64(SockList *sl, uint64 data) { 00138 SockList_Ensure(sl, 8); 00139 sl->buf[sl->len++] = (char)((data>>56)&0xff); 00140 sl->buf[sl->len++] = (char)((data>>48)&0xff); 00141 sl->buf[sl->len++] = (char)((data>>40)&0xff); 00142 sl->buf[sl->len++] = (char)((data>>32)&0xff); 00143 sl->buf[sl->len++] = (char)((data>>24)&0xff); 00144 sl->buf[sl->len++] = (char)((data>>16)&0xff); 00145 sl->buf[sl->len++] = (char)((data>>8)&0xff); 00146 sl->buf[sl->len++] = (char)(data&0xff); 00147 } 00148 00154 void SockList_AddString(SockList *sl, const char *data) { 00155 SockList_AddData(sl, data, strlen(data)); 00156 } 00157 00164 void SockList_AddData(SockList *sl, const void *data, size_t len) { 00165 SockList_Ensure(sl, len); 00166 memcpy(sl->buf+sl->len, data, len); 00167 sl->len += len; 00168 } 00169 00176 void SockList_AddLen8Data(SockList *sl, const void *data, size_t len) { 00177 assert(len <= 255); 00178 SockList_AddChar(sl, len); 00179 SockList_AddData(sl, data, len); 00180 } 00181 00187 void SockList_AddPrintf(SockList *sl, const char *format, ...) { 00188 size_t size; 00189 int n; 00190 va_list arg; 00191 00192 size = sizeof(sl->buf)-sl->len; 00193 00194 va_start(arg, format); 00195 n = vsnprintf((char *)sl->buf+sl->len, size, format, arg); 00196 va_end(arg); 00197 00198 if (n <= -1 || (size_t)n >= size) { 00199 fatal(OUT_OF_MEMORY); 00200 } 00201 sl->len += (size_t)n; 00202 } 00203 00210 void SockList_AddStringBuffer(SockList *sl, StringBuffer *sb) { 00211 char *p; 00212 00213 p = stringbuffer_finish(sb); 00214 SockList_AddString(sl, p); 00215 free(p); 00216 } 00217 00222 void SockList_NullTerminate(SockList *sl) { 00223 SockList_Ensure(sl, 1); 00224 sl->buf[sl->len] = '\0'; 00225 } 00226 00231 size_t SockList_Avail(const SockList *sl) { 00232 return sizeof(sl->buf)-sl->len; 00233 } 00234 00239 int GetInt_String(const unsigned char *data) { 00240 return ((data[0]<<24)+(data[1]<<16)+(data[2]<<8)+data[3]); 00241 } 00242 00243 short GetShort_String(const unsigned char *data) { 00244 return ((data[0]<<8)+data[1]); 00245 } 00246 00247 /****************************************************************************** 00248 * 00249 * Start of read routines. 00250 * 00251 ******************************************************************************/ 00252 00260 int SockList_ReadPacket(int fd, SockList *sl, int len) { 00261 int stat, toread; 00262 char err[MAX_BUF]; 00263 00264 /* We already have a partial packet */ 00265 if (sl->len < 2) { 00266 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: change read() to recv() */ 00267 00268 stat = recv(fd, sl->buf+sl->len, 2-sl->len, 0); 00269 00270 #else 00271 do { 00272 stat = read(fd, sl->buf+sl->len, 2-sl->len); 00273 } while ((stat == -1) && (errno == EINTR)); 00274 #endif 00275 if (stat < 0) { 00276 /* In non blocking mode, EAGAIN is set when there is no 00277 * data available. 00278 */ 00279 #ifdef WIN32 /* ***WIN32 SockList_ReadPacket: error handling for win32 */ 00280 if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) { 00281 if (WSAGetLastError() == WSAECONNRESET) 00282 LOG(llevDebug, "Connection closed by client\n"); 00283 else { 00284 LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError()); 00285 } 00286 return -1; /* kick this user! */ 00287 } 00288 #else 00289 if (errno == ECONNRESET) { 00290 LOG(llevDebug, "ReadPacket got error %s, returning -1\n", strerror_local(errno, err, sizeof(err))); 00291 return -1; 00292 } 00293 if (errno != EAGAIN && errno != EWOULDBLOCK) { 00294 LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror_local(errno, err, sizeof(err))); 00295 } 00296 #endif 00297 return 0; /*Error */ 00298 } 00299 if (stat == 0) 00300 return -1; 00301 sl->len += stat; 00302 #ifdef CS_LOGSTATS 00303 cst_tot.ibytes += stat; 00304 cst_lst.ibytes += stat; 00305 #endif 00306 if (stat < 2) 00307 return 0; /* Still don't have a full packet */ 00308 } 00309 /* Figure out how much more data we need to read. Add 2 from the 00310 * end of this - size header information is not included. 00311 */ 00312 toread = 2+(sl->buf[0]<<8)+sl->buf[1]-sl->len; 00313 if ((toread+(int)sl->len) >= len) { 00314 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); 00315 /* Quick hack in case for 'oldsocketmode' input. If we are 00316 * closing the socket anyways, then reading this extra 100 bytes 00317 * shouldn't hurt. 00318 */ 00319 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */ 00320 recv(fd, sl->buf+2, 100, 0); 00321 #else 00322 read(fd, sl->buf+2, 100); 00323 #endif /* end win32 */ 00324 00325 /* return error so the socket is closed */ 00326 return -1; 00327 } 00328 do { 00329 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change read() to recv() */ 00330 stat = recv(fd, sl->buf+sl->len, toread, 0); 00331 #else 00332 do { 00333 stat = read(fd, sl->buf+sl->len, toread); 00334 } while ((stat < 0) && (errno == EINTR)); 00335 #endif 00336 if (stat < 0) { 00337 #ifdef WIN32 /* ***win32 SockList_ReadPacket: change error handling for win32 */ 00338 if ((stat == -1) && WSAGetLastError() != WSAEWOULDBLOCK) { 00339 if (WSAGetLastError() == WSAECONNRESET) 00340 LOG(llevDebug, "Connection closed by client\n"); 00341 else { 00342 LOG(llevDebug, "ReadPacket got error %d, returning -1\n", WSAGetLastError()); 00343 } 00344 return -1; /* kick this user! */ 00345 } 00346 #else 00347 if (errno != EAGAIN && errno != EWOULDBLOCK) { 00348 LOG(llevDebug, "ReadPacket got error %s, returning 0\n", strerror_local(errno, err, sizeof(err))); 00349 } 00350 #endif 00351 return 0; /*Error */ 00352 } 00353 if (stat == 0) 00354 return -1; 00355 sl->len += stat; 00356 #ifdef CS_LOGSTATS 00357 cst_tot.ibytes += stat; 00358 cst_lst.ibytes += stat; 00359 #endif 00360 toread -= stat; 00361 if (toread == 0) 00362 return 1; 00363 if (toread < 0) { 00364 LOG(llevError, "SockList_ReadPacket: Read more bytes than desired.\n"); 00365 return 1; 00366 } 00367 } while (toread > 0); 00368 return 0; 00369 } 00370 00371 /******************************************************************************* 00372 * 00373 * Start of write related routines. 00374 * 00375 ******************************************************************************/ 00376 00383 static void add_to_buffer(socket_struct *ns, const unsigned char *buf, int len) { 00384 int avail, end; 00385 00386 if ((len+ns->outputbuffer.len) > SOCKETBUFSIZE) { 00387 LOG(llevDebug, "Socket on fd %d has overrun internal buffer - marking as dead\n", ns->fd); 00388 ns->status = Ns_Dead; 00389 return; 00390 } 00391 00392 /* data + end is where we start putting the new data. The last byte 00393 * currently in use is actually data + end -1 00394 */ 00395 00396 end = ns->outputbuffer.start+ns->outputbuffer.len; 00397 /* The buffer is already in a wrapped state, so adjust end */ 00398 if (end >= SOCKETBUFSIZE) 00399 end -= SOCKETBUFSIZE; 00400 avail = SOCKETBUFSIZE-end; 00401 00402 /* We can all fit it behind the current data without wrapping */ 00403 if (avail >= len) { 00404 memcpy(ns->outputbuffer.data+end, buf, len); 00405 } else { 00406 memcpy(ns->outputbuffer.data+end, buf, avail); 00407 memcpy(ns->outputbuffer.data, buf+avail, len-avail); 00408 } 00409 ns->outputbuffer.len += len; 00410 } 00411 00418 void write_socket_buffer(socket_struct *ns) { 00419 int amt, max; 00420 00421 if (ns->outputbuffer.len == 0) { 00422 LOG(llevDebug, "write_socket_buffer called when there is no data, fd=%d\n", ns->fd); 00423 return; 00424 } 00425 00426 do { 00427 max = SOCKETBUFSIZE-ns->outputbuffer.start; 00428 if (ns->outputbuffer.len < max) 00429 max = ns->outputbuffer.len; 00430 00431 #ifdef WIN32 /* ***win32 write_socket_buffer: change write() to send() */ 00432 amt = send(ns->fd, ns->outputbuffer.data+ns->outputbuffer.start, max, 0); 00433 #else 00434 do { 00435 amt = write(ns->fd, ns->outputbuffer.data+ns->outputbuffer.start, max); 00436 } while ((amt < 0) && (errno == EINTR)); 00437 #endif 00438 00439 if (amt < 0) { /* We got an error */ 00440 00441 #ifdef WIN32 /* ***win32 write_socket_buffer: change error handling */ 00442 if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) { 00443 LOG(llevError, "New socket write failed (wsb) (%d).\n", WSAGetLastError()); 00444 #else 00445 if (errno != EWOULDBLOCK) { 00446 char err[MAX_BUF]; 00447 00448 LOG(llevError, "New socket write failed (wsb) (%d: %s).\n", errno, strerror_local(errno, err, sizeof(err))); 00449 #endif 00450 ns->status = Ns_Dead; 00451 return; 00452 } else { /* EWOULDBLOCK */ 00453 /* can't write it, so store it away. */ 00454 ns->can_write = 0; 00455 return; 00456 } 00457 } 00458 ns->outputbuffer.start += amt; 00459 /* wrap back to start of buffer */ 00460 if (ns->outputbuffer.start == SOCKETBUFSIZE) 00461 ns->outputbuffer.start = 0; 00462 ns->outputbuffer.len -= amt; 00463 #ifdef CS_LOGSTATS 00464 cst_tot.obytes += amt; 00465 cst_lst.obytes += amt; 00466 #endif 00467 } while (ns->outputbuffer.len > 0); 00468 } 00469 00477 static void Write_To_Socket(socket_struct *ns, const unsigned char *buf, int len) { 00478 int amt = 0; 00479 const unsigned char *pos = buf; 00480 char err[MAX_BUF]; 00481 00482 if (ns->status == Ns_Dead || !buf) { 00483 LOG(llevDebug, "Write_To_Socket called with dead socket\n"); 00484 return; 00485 } 00486 00487 #ifndef __GNU__ /* This caused problems on Hurd */ 00488 if (!ns->can_write) { 00489 add_to_buffer(ns, buf, len); 00490 return; 00491 } 00492 #endif 00493 /* If we manage to write more than we wanted, take it as a bonus */ 00494 while (len > 0) { 00495 00496 #ifdef WIN32 /* ***win32 Write_To_Socket: change write() to send() */ 00497 amt = send(ns->fd, pos, len, 0); 00498 #else 00499 do { 00500 amt = write(ns->fd, pos, len); 00501 } while ((amt < 0) && (errno == EINTR)); 00502 #endif 00503 00504 if (amt < 0) { /* We got an error */ 00505 #ifdef WIN32 /* ***win32 Write_To_Socket: change error handling */ 00506 if (amt == -1 && WSAGetLastError() != WSAEWOULDBLOCK) { 00507 LOG(llevError, "New socket write failed WTS (%d).\n", WSAGetLastError()); 00508 #else 00509 if (errno != EWOULDBLOCK) { 00510 LOG(llevError, "New socket write failed WTS (%d: %s).\n", errno, strerror_local(errno, err, sizeof(err))); 00511 #endif 00512 ns->status = Ns_Dead; 00513 return; 00514 } else { /* EWOULDBLOCK */ 00515 /* can't write it, so store it away. */ 00516 add_to_buffer(ns, pos, len); 00517 ns->can_write = 0; 00518 return; 00519 } 00520 /* amt gets set to 0 above in blocking code, so we do this as 00521 * an else if to make sure we don't reprocess it. 00522 */ 00523 } else if (amt == 0) { 00524 LOG(llevError, "Write_To_Socket: No data written out.\n"); 00525 } 00526 len -= amt; 00527 pos += amt; 00528 #ifdef CS_LOGSTATS 00529 cst_tot.obytes += amt; 00530 cst_lst.obytes += amt; 00531 #endif 00532 } 00533 } 00534 00541 void Send_With_Handling(socket_struct *ns, SockList *sl) { 00542 if (ns->status == Ns_Dead || sl == NULL) 00543 return; 00544 00545 sl->buf[0] = ((sl->len-2)>>8)&0xFF; 00546 sl->buf[1] = (sl->len-2)&0xFF; 00547 Write_To_Socket(ns, sl->buf, sl->len); 00548 } 00549 00550 /****************************************************************************** 00551 * 00552 * statistics logging functions. 00553 * 00554 ******************************************************************************/ 00555 00556 #ifdef CS_LOGSTATS 00557 /* cst_tot is for the life of the server, cst_last is for the last series of 00558 * stats 00559 */ 00560 CS_Stats cst_tot, cst_lst; 00561 00565 void write_cs_stats(void) { 00566 time_t now = time(NULL); 00567 00568 /* If no connections recently, don't both to log anything */ 00569 if (cst_lst.ibytes == 0 && cst_lst.obytes == 0) 00570 return; 00571 00572 /* CSSTAT is put in so scripts can easily find the line */ 00573 LOG(llevInfo, "CSSTAT: %.16s tot %d %d %d %ld inc %d %d %d %ld\n", 00574 ctime(&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn, 00575 (long)(now-cst_tot.time_start), cst_lst.ibytes, cst_lst.obytes, 00576 cst_lst.max_conn, (long)(now-cst_lst.time_start)); 00577 cst_lst.ibytes = 0; 00578 cst_lst.obytes = 0; 00579 cst_lst.max_conn = socket_info.nconns; 00580 cst_lst.time_start = now; 00581 } 00582 #endif 00583 00584 static void SockList_Ensure(const SockList *sl, size_t size) { 00585 if (sl->len+size > sizeof(sl->buf)) { 00586 fatal(OUT_OF_MEMORY); 00587 } 00588 }