Atrinik Server 2.5
socket/lowlevel.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 #include <zlib.h>
00032 
00037 void SockList_AddString(SockList *sl, const char *data)
00038 {
00039     char c;
00040 
00041     while ((c = *data++))
00042     {
00043         sl->buf[sl->len] = c;
00044         sl->len++;
00045     }
00046 
00047     sl->buf[sl->len] = c;
00048     sl->len++;
00049 }
00050 
00059 char *GetString_String(uint8 *data, int len, int *pos, char *dest, size_t dest_size)
00060 {
00061     size_t i = 0;
00062     char c;
00063 
00064     while (*pos < len && (c = (char) (data[(*pos)++])))
00065     {
00066         if (i < dest_size - 1)
00067         {
00068             dest[i++] = c;
00069         }
00070     }
00071 
00072     dest[i] = '\0';
00073     return dest;
00074 }
00075 
00081 int SockList_ReadPacket(socket_struct *ns, int len)
00082 {
00083     SockList *sl = &ns->readbuf;
00084     int stat_ret;
00085 
00086 #ifdef WIN32
00087     stat_ret = recv(ns->fd, sl->buf + sl->len, len - sl->len, 0);
00088 #else
00089     do
00090     {
00091         stat_ret = read(ns->fd, sl->buf + sl->len, len - sl->len);
00092     }
00093     while (stat_ret == -1 && errno == EINTR);
00094 #endif
00095 
00096     if (stat_ret == 0)
00097     {
00098         return -1;
00099     }
00100 
00101     if (stat_ret > 0)
00102     {
00103         sl->len += stat_ret;
00104 #if CS_LOGSTATS
00105         cst_tot.ibytes += stat_ret;
00106         cst_lst.ibytes += stat_ret;
00107 #endif
00108     }
00109     else if (stat_ret < 0)
00110     {
00111 #ifdef WIN32
00112         if (WSAGetLastError() != WSAEWOULDBLOCK)
00113         {
00114             if (WSAGetLastError() == WSAECONNRESET)
00115             {
00116                 LOG(llevDebug, "Connection closed by client.\n");
00117             }
00118             else
00119             {
00120                 LOG(llevDebug, "SockList_ReadPacket() got error %d, returning %d.\n", WSAGetLastError(), stat_ret);
00121             }
00122 
00123             return stat_ret;
00124         }
00125 #else
00126         if (errno != EINTR && errno != EAGAIN && errno != EWOULDBLOCK)
00127         {
00128             LOG(llevDebug, "SockList_ReadPacket() got error %d: %s, returning %d.\n", errno, strerror_local(errno), stat_ret);
00129             return stat_ret;
00130         }
00131 #endif
00132     }
00133 
00134     return 1;
00135 }
00136 
00142 int SockList_ReadCommand(SockList *sl, SockList *sl2)
00143 {
00144     int toread, ret = 0;
00145 
00146     sl2->buf[0] = '\0';
00147     sl2->len = 0;
00148 
00149     /* Is there anything in our buffer that was read
00150      * before? */
00151     if (sl->len >= 2)
00152     {
00153         /* Length of the command. */
00154         toread = 2 + (sl->buf[0] << 8) + sl->buf[1];
00155 
00156         /* If we have a command, copy it over. */
00157         if (toread <= sl->len)
00158         {
00159             memcpy(sl2->buf, sl->buf, toread);
00160             sl2->len = toread;
00161 
00162             if (sl->len - toread)
00163             {
00164                 memmove(sl->buf, sl->buf + toread, sl->len - toread);
00165             }
00166 
00167             sl->len -= toread;
00168             ret = toread;
00169         }
00170     }
00171 
00172     return ret;
00173 }
00174 
00178 void socket_enable_no_delay(int fd)
00179 {
00180     int tmp = 1;
00181 
00182     if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &tmp, sizeof(tmp)))
00183     {
00184         LOG(llevDebug, "socket_enable_no_delay(): Cannot enable TCP_NODELAY: %s\n", strerror_local(errno));
00185     }
00186 }
00187 
00191 void socket_disable_no_delay(int fd)
00192 {
00193     int tmp = 0;
00194 
00195     if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, (char *) &tmp, sizeof(tmp)))
00196     {
00197         LOG(llevDebug, "socket_disable_no_delay(): Cannot disable TCP_NODELAY: %s\n", strerror_local(errno));
00198     }
00199 }
00200 
00207 static void socket_buffer_enqueue(socket_struct *ns, unsigned char *buf, size_t len, uint8 ndelay)
00208 {
00209     socket_buffer *buffer = (socket_buffer *) malloc(sizeof(socket_buffer));
00210 
00211     buffer->buf = (char *) malloc(len + 1);
00212     memcpy(buffer->buf, buf, len);
00213     buffer->len = len;
00214     buffer->next = NULL;
00215     buffer->pos = 0;
00216     buffer->ndelay = ndelay;
00217 
00218     if (ns->buffer_front)
00219     {
00220         ns->buffer_front->last = buffer;
00221         buffer->next = ns->buffer_front;
00222         buffer->last = NULL;
00223         ns->buffer_front = buffer;
00224     }
00225     else
00226     {
00227         ns->buffer_back = ns->buffer_front = buffer;
00228         buffer->next = buffer->last = NULL;
00229     }
00230 }
00231 
00235 static void socket_buffer_dequeue(socket_struct *ns)
00236 {
00237     socket_buffer *tmp = ns->buffer_back;
00238 
00239     ns->buffer_back = ns->buffer_back->last;
00240 
00241     if (ns->buffer_back)
00242     {
00243         ns->buffer_back->next = NULL;
00244     }
00245     else
00246     {
00247         ns->buffer_front = NULL;
00248     }
00249 
00250     free(tmp->buf);
00251     free(tmp);
00252 }
00253 
00257 void socket_buffer_clear(socket_struct *ns)
00258 {
00259     while (ns->buffer_back)
00260     {
00261         socket_buffer_dequeue(ns);
00262     }
00263 }
00264 
00268 void socket_buffer_write(socket_struct *ns)
00269 {
00270     int amt, max;
00271 
00272     /* Nothing to send? */
00273     if (!ns->buffer_back)
00274     {
00275         return;
00276     }
00277 
00278     while (ns->buffer_back)
00279     {
00280         if (ns->buffer_back->ndelay)
00281         {
00282             socket_enable_no_delay(ns->fd);
00283         }
00284 
00285         max = ns->buffer_back->len - ns->buffer_back->pos;
00286         amt = send(ns->fd, ns->buffer_back->buf + ns->buffer_back->pos, max, MSG_DONTWAIT);
00287 
00288         if (ns->buffer_back->ndelay)
00289         {
00290             socket_disable_no_delay(ns->fd);
00291         }
00292 
00293 #ifndef WIN32
00294         if (!amt)
00295         {
00296             amt = max;
00297         }
00298         else
00299 #endif
00300         if (amt < 0)
00301         {
00302 #ifdef WIN32
00303             if (WSAGetLastError() != WSAEWOULDBLOCK)
00304             {
00305                 LOG(llevDebug, "socket_buffer_write(): New socket write failed (%d).\n", WSAGetLastError());
00306 #else
00307             if (errno != EWOULDBLOCK)
00308             {
00309                 LOG(llevDebug, "socket_buffer_write(): New socket write failed (%d: %s).\n", errno, strerror_local(errno));
00310 #endif
00311                 ns->status = Ns_Dead;
00312                 return;
00313             }
00314             /* EWOULDBLOCK: We can't write because socket is busy. */
00315             else
00316             {
00317                 return;
00318             }
00319         }
00320 
00321         ns->buffer_back->pos += amt;
00322 #if CS_LOGSTATS
00323         cst_tot.obytes += amt;
00324         cst_lst.obytes += amt;
00325 #endif
00326 
00327         if (ns->buffer_back->len - ns->buffer_back->pos == 0)
00328         {
00329             socket_buffer_dequeue(ns);
00330         }
00331     }
00332 }
00333 
00341 void Send_With_Handling(socket_struct *ns, SockList *msg)
00342 {
00343     unsigned char sbuf[4];
00344     uint8 *buf;
00345     size_t len;
00346 #if COMPRESS_DATA_PACKETS
00347     uint8 *dest = NULL;
00348 #endif
00349 
00350     if (ns->status == Ns_Dead || !msg)
00351     {
00352         return;
00353     }
00354 
00355     buf = msg->buf;
00356     len = msg->len;
00357 
00358 #if COMPRESS_DATA_PACKETS
00359     if (len > COMPRESS_DATA_PACKETS_SIZE && buf[0] != BINARY_CMD_DATA)
00360     {
00361         size_t new_size = compressBound(len);
00362 
00363         dest = malloc(new_size + 5);
00364         /* Marker for the reserved #0 binary command. */
00365         dest[0] = 0;
00366         /* Add original length of the packet. */
00367         dest[1] = (len >> 24) & 0xff;
00368         dest[2] = (len >> 16) & 0xff;
00369         dest[3] = (len >> 8) & 0xff;
00370         dest[4] = (len) & 0xff;
00371         /* Compress it. */
00372         compress2((Bytef *) dest + 5, (uLong *) &new_size, (const unsigned char FAR *) buf, len, Z_BEST_COMPRESSION);
00373         buf = dest;
00374         len = new_size + 5;
00375     }
00376 #endif
00377 
00378     /* If more than 32kb use 3 bytes header and set the high bit to show
00379      * it to the client. */
00380     if (len > 32 * 1024 - 1)
00381     {
00382         sbuf[0] = ((uint32) (len) >> 16) & 0xFF;
00383         /* High bit marker for the client */
00384         sbuf[0] |= 0x80;
00385         sbuf[1] = ((uint32) (len) >> 8) & 0xFF;
00386         sbuf[2] = ((uint32) (len)) & 0xFF;
00387 
00388         socket_buffer_enqueue(ns, sbuf, 3, 0);
00389     }
00390     else
00391     {
00392         sbuf[0] = ((uint32) (len) >> 8) & 0xFF;
00393         sbuf[1] = ((uint32) (len)) & 0xFF;
00394 
00395         socket_buffer_enqueue(ns, sbuf, 2, 0);
00396     }
00397 
00398     socket_buffer_enqueue(ns, buf, len, msg->buf[0] == BINARY_CMD_MAP2 || msg->buf[0] == BINARY_CMD_ITEMY);
00399 
00400 #if COMPRESS_DATA_PACKETS
00401     /* Free the buffer that was used for compression, if it was allocated. */
00402     if (dest)
00403     {
00404         free(dest);
00405     }
00406 #endif
00407 }
00408 
00412 void Write_String_To_Socket(socket_struct *ns, char cmd, const char *buf, int len)
00413 {
00414     SockList sl;
00415 
00416     sl.len = len;
00417     sl.buf = (uint8 *) buf;
00418     *((char *) buf) = cmd;
00419 
00420     Send_With_Handling(ns, &sl);
00421 }
00422 
00423 #if CS_LOGSTATS
00424 
00426 CS_Stats cst_tot;
00427 
00429 CS_Stats cst_lst;
00430 
00435 void write_cs_stats()
00436 {
00437     time_t now = time(NULL);
00438 
00439     /* If no connections recently, don't bother to log anything */
00440     if (cst_lst.ibytes == 0 && cst_lst.obytes == 0)
00441     {
00442         return;
00443     }
00444 
00445     /* CSSTAT is put in so scripts can easily find the line */
00446     LOG(llevInfo, "CSSTAT: %.16s tot in:%d out:%d maxc:%d time:%lu last block-> in:%d out:%d maxc:%d time:%lu\n", ctime(&now), cst_tot.ibytes, cst_tot.obytes, cst_tot.max_conn, now - cst_tot.time_start, cst_lst.ibytes, cst_lst.obytes, cst_lst.max_conn, now - cst_lst.time_start);
00447 
00448     cst_lst.ibytes = 0;
00449     cst_lst.obytes = 0;
00450     cst_lst.max_conn = socket_info.nconns;
00451     cst_lst.time_start = now;
00452 }
00453 #endif