Crossfire Server, Branch 1.12  R12190
lowlevel.c
Go to the documentation of this file.
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 }